<?php

namespace App\Domains\Event\Services;

use App\Domains\Tenant\Models\Tenant;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class EventService
{
    private const CONTACT_CODES = [
        'whatsapp_url' => 'whatsapp',
        'instagram_url' => 'instagram',
        'facebook_url' => 'facebook',
    ];

    public function forTenant(Tenant $tenant): Tenant
    {
        return $tenant->load(['eventDates.validityTime', 'socialMedia']);
    }

    /** @param array<string, mixed> $data */
    public function updateForTenant(Tenant $tenant, array $data): Tenant
    {
        return DB::transaction(function () use ($tenant, $data): Tenant {
            $tenant = Tenant::query()->whereKey($tenant->getKey())->lockForUpdate()->firstOrFail();
            $tenant->update([
                'event_title' => $data['title'],
                'event_location' => $data['location'],
            ]);

            $this->syncDates($tenant, $data['dates']);
            if (array_key_exists('social_media', $data)) {
                $this->syncSocialMedia($tenant, $data['social_media']);
            } else {
                $this->syncLegacyContact($tenant, $data['contact']);
            }

            return $tenant->load(['eventDates.validityTime', 'socialMedia']);
        });
    }

    /** @param array<int, array{date: string, start_time: string, end_time: string}> $dates */
    private function syncDates(Tenant $tenant, array $dates): void
    {
        $existingDates = $tenant->eventDates()->get()->values();

        foreach (array_values($dates) as $index => $date) {
            $attributes = [
                'date' => $date['date'],
                'time_start' => $date['start_time'],
                'time_end' => $date['end_time'],
            ];

            $existingDate = $existingDates->get($index);

            if ($existingDate) {
                $existingDate->update($attributes);
            } else {
                $tenant->eventDates()->create($attributes);
            }
        }

        $datesToDelete = $existingDates->slice(count($dates));

        if ($datesToDelete->contains(fn ($eventDate): bool => $eventDate
            ->selectedByVariants()
            ->whereHas('sourceTickets')
            ->exists()
            || $eventDate->variants()->whereHas('sourceTickets')->exists())) {
            throw ValidationException::withMessages([
                'dates' => ['No se puede eliminar una fecha utilizada por tickets generados.'],
            ]);
        }

        $datesToDelete->each->delete();
        $tenant->unsetRelation('eventDates');
    }

    /** @param array<string, string|null> $contact */
    private function syncLegacyContact(Tenant $tenant, array $contact): void
    {
        foreach (self::CONTACT_CODES as $field => $code) {
            $url = $contact[$field] ?? null;

            if ($url === null || $url === '') {
                $tenant->socialMedia()->detach($code);

                continue;
            }

            $tenant->socialMedia()->syncWithoutDetaching([
                $code => ['url' => $url],
            ]);
        }

        $tenant->unsetRelation('socialMedia');
    }

    /** @param array<int, array{code: string, url: string, orden?: int}> $socialMedia */
    private function syncSocialMedia(Tenant $tenant, array $socialMedia): void
    {
        $associations = [];

        foreach (array_values($socialMedia) as $index => $item) {
            $associations[$item['code']] = [
                'url' => $item['url'],
                'orden' => $item['orden'] ?? $index,
            ];
        }

        $tenant->socialMedia()->sync($associations);
        $tenant->unsetRelation('socialMedia');
    }
}
