<?php

namespace App\Livewire\Events;

use App\Domain\Event\Enums\EventType;
use App\Domain\Event\Enums\FormFieldType;
use App\Domain\Event\Enums\LocationType;
use App\Domain\Event\Models\Event;
use App\Domain\Event\Services\EventService;
use App\Domain\Facility\Models\Facility;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use App\Domain\Website\Enums\MediaCollection;
use App\Domain\Website\Services\MediaService;
use Illuminate\Validation\Rule;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;
use App\Domain\Shared\Traits\ChecksTemporaryUploads;
use Livewire\WithFileUploads;

#[Layout('layouts.app')]
#[Title('إنشاء حدث')]
class CreateEventWizard extends Component
{
    use WithFileUploads, UsesBranchScope;
    use ChecksTemporaryUploads;

    #[Locked]
    public ?Event $event = null;

    #[Locked]
    public bool $editing = false;
    public int $currentStep = 1;
    public int $totalSteps = 4;
    public bool $completed = false;

    // Step 1: Details
    public string $title = '';
    public string $titleEn = '';
    public string $description = '';
    public string $descriptionEn = '';
    public string $type = '';
    public string $locationType = 'facility';
    public ?int $facilityId = null;
    public string $locationName = '';
    public string $locationAddress = '';
    public array $mapLocations = [];
    public string $startsAt = '';
    public string $endsAt = '';
    public string $registrationOpensAt = '';
    public string $registrationClosesAt = '';
    public ?int $maxCapacity = null;

    // Step 2: Form Fields
    public array $formFields = [];

    // Step 3: Media
    public $coverPhoto = null;
    public array $galleryPhotos = [];

    public function mount(?Event $event = null): void
    {
        if ($event && $event->exists) {
            $this->authorize('events.update');
            $this->event = $event;
            $this->editing = true;
            $this->fillFromEvent($event);
        } else {
            $this->authorize('events.create');
            $this->formFields = $this->getDefaultFields();
        }
    }

    private function fillFromEvent(Event $event): void
    {
        $this->title = $event->title;
        $this->titleEn = $event->title_en ?? '';
        $this->description = $event->description ?? '';
        $this->descriptionEn = $event->description_en ?? '';
        $this->type = $event->type->value;
        $this->locationType = $event->location_type->value;
        $this->facilityId = $event->facility_id;
        $this->locationName = $event->location_name ?? '';
        $this->locationAddress = $event->location_address ?? '';
        $this->mapLocations = $event->map_locations ?: [];
        $this->startsAt = $event->starts_at?->format('Y-m-d\TH:i');
        $this->endsAt = $event->ends_at?->format('Y-m-d\TH:i');
        $this->registrationOpensAt = $event->registration_opens_at?->format('Y-m-d\TH:i') ?? '';
        $this->registrationClosesAt = $event->registration_closes_at?->format('Y-m-d\TH:i') ?? '';
        $this->maxCapacity = $event->max_capacity;
        $this->formFields = $event->form_fields ?: $this->getDefaultFields();
    }

    private function getDefaultFields(): array
    {
        return [
            $this->makeField('name', 'text', 'الاسم الكامل', true, 'full', ['label_en' => 'Full Name']),
            $this->makeField('phone', 'phone', 'رقم الهاتف', true, 'half', ['label_en' => 'Phone', 'placeholder' => '01XXXXXXXXX', 'is_unique' => true]),
            $this->makeField('email', 'email', 'البريد الإلكتروني', false, 'half', ['label_en' => 'Email']),
        ];
    }

    private function makeField(string $key, string $type, string $label, bool $required, string $width, array $extra = []): array
    {
        return array_merge([
            'key' => $key,
            'type' => $type,
            'label' => $label,
            'label_en' => '',
            'placeholder' => '',
            'description' => '',
            'is_required' => $required,
            'is_unique' => false,
            'sort_order' => 0,
            'options' => [],
            'width' => $width,
            'allow_other' => false,
            'min_length' => null,
            'max_length' => null,
            'min_value' => null,
            'max_value' => null,
            'default_value' => '',
            'pattern' => '',
            'pattern_message' => '',
            'condition_field' => '',
            'condition_value' => '',
            'terms_url' => '',
        ], $extra);
    }

    public function nextStep(): void
    {
        $this->validateStep();
        $this->currentStep = min($this->currentStep + 1, $this->totalSteps);
    }

    public function previousStep(): void
    {
        $this->currentStep = max($this->currentStep - 1, 1);
    }

    public function goToStep(int $step): void
    {
        if ($step <= $this->currentStep) {
            $this->currentStep = $step;
        }
    }

    private function validateStep(?int $step = null): void
    {
        $rules = match ($step ?? $this->currentStep) {
            1 => [
                'title' => 'required|string|max:255',
                'type' => 'required|in:' . implode(',', array_column(EventType::cases(), 'value')),
                'locationType' => 'required|in:facility,external',
                'facilityId' => $this->locationType === 'facility'
                    ? ['required', $this->facilityExistsRule()]
                    : ['nullable'],
                'locationName' => $this->locationType === 'external' ? 'required|string|max:255' : 'nullable',
                'startsAt' => 'required|date',
                'endsAt' => 'required|date|after:startsAt',
            ],
            2 => [],
            3 => [
                'coverPhoto' => 'nullable|image|max:2048',
            ],
            default => [],
        };

        if (! empty($rules)) {
            // Both upload properties: a cover picked on step 3 and gallery
            // photos added earlier can each outlive their temporary file.
            foreach (['coverPhoto' => $this->coverPhoto, 'galleryPhotos' => $this->galleryPhotos] as $field => $pending) {
                if (! $this->temporaryUploadIsUsable($field, $pending)) {
                    return;
                }
            }

            $this->validate($rules, [
                'title.required' => 'عنوان الحدث مطلوب',
                'type.required' => 'نوع الحدث مطلوب',
                'locationType.required' => 'نوع الموقع مطلوب',
                'facilityId.required' => 'يرجى اختيار المنشأة',
                'locationName.required' => 'اسم الموقع مطلوب',
                'startsAt.required' => 'تاريخ البداية مطلوب',
                'endsAt.required' => 'تاريخ النهاية مطلوب',
                'endsAt.after' => 'تاريخ النهاية يجب أن يكون بعد تاريخ البداية',
            ]);
        }
    }

    /**
     * `exists:facilities,id` is a raw table query: it goes around both the
     * academy and the branch global scopes, and $facilityId arrives from the
     * browser bound to a <select>. Rebuild it so the only ids it accepts are
     * the ones that <select> could legitimately have offered — otherwise an
     * event in this branch can be filed against another branch's facility,
     * which moves the event out of this branch's list entirely.
     *
     * A null branch means the user is in all-branches mode and is entitled to
     * every facility, so the branch clause is simply dropped there.
     */
    private function facilityExistsRule(): \Illuminate\Validation\Rules\Exists
    {
        $branchId = $this->getActiveBranchId();

        return Rule::exists('facilities', 'id')
            ->where('academy_id', app('current_academy')->id)
            ->whereNull('deleted_at')
            ->when(
                $branchId !== null,
                fn ($rule) => $rule->where('branch_id', $branchId)
            );
    }

    // ========== Form Builder Actions ==========

    public function addField(): void
    {
        $order = count($this->formFields) + 1;
        $this->formFields[] = $this->makeField('field_' . $order, 'text', '', false, 'full');
        $this->reorderFields();
    }

    public function duplicateField(int $index): void
    {
        if (!isset($this->formFields[$index])) return;

        $clone = $this->formFields[$index];
        $clone['key'] = $clone['key'] . '_copy';
        $clone['label'] = $clone['label'] . ' (نسخة)';

        array_splice($this->formFields, $index + 1, 0, [$clone]);
        $this->reorderFields();
    }

    public function removeField(int $index): void
    {
        unset($this->formFields[$index]);
        $this->formFields = array_values($this->formFields);
        $this->reorderFields();
    }

    public function moveFieldUp(int $index): void
    {
        if ($index <= 0) return;

        $temp = $this->formFields[$index];
        $this->formFields[$index] = $this->formFields[$index - 1];
        $this->formFields[$index - 1] = $temp;
        $this->reorderFields();
    }

    public function moveFieldDown(int $index): void
    {
        if ($index >= count($this->formFields) - 1) return;

        $temp = $this->formFields[$index];
        $this->formFields[$index] = $this->formFields[$index + 1];
        $this->formFields[$index + 1] = $temp;
        $this->reorderFields();
    }

    public function addOption(int $fieldIndex): void
    {
        if (!isset($this->formFields[$fieldIndex])) return;
        $this->formFields[$fieldIndex]['options'][] = ['value' => '', 'label' => ''];
    }

    public function removeOption(int $fieldIndex, int $optionIndex): void
    {
        if (!isset($this->formFields[$fieldIndex]['options'][$optionIndex])) return;
        unset($this->formFields[$fieldIndex]['options'][$optionIndex]);
        $this->formFields[$fieldIndex]['options'] = array_values($this->formFields[$fieldIndex]['options']);
    }

    private function reorderFields(): void
    {
        foreach ($this->formFields as $i => &$field) {
            $field['sort_order'] = $i + 1;
            if (empty($field['key'])) {
                $field['key'] = 'field_' . ($i + 1);
            }
        }
    }

    // ========== Map Locations ==========

    public function addMapLocation(): void
    {
        $this->mapLocations[] = [
            'title' => '',
            'lat' => '',
            'lng' => '',
            'address' => '',
        ];
    }

    public function removeMapLocation(int $index): void
    {
        unset($this->mapLocations[$index]);
        $this->mapLocations = array_values($this->mapLocations);
    }

    // ========== Save ==========

    public function save(EventService $eventService, MediaService $mediaService): void
    {
        // Every step, not just the one the wizard says it is on. save() is
        // reached from step 4, whose rule set is empty, so validateStep() alone
        // checked nothing at all — facilityId went to facility_id with
        // facilityExistsRule() (the branch check) never running at the write.
        // currentStep is a plain public property besides, so the browser can
        // name any step it likes before submitting.
        foreach (range(1, $this->totalSteps) as $step) {
            $this->validateStep($step);
        }

        $this->sanitizeFormFields();

        $academyId = app('current_academy')->id;

        $data = [
            'academy_id' => $academyId,
            'title' => $this->title,
            'title_en' => $this->titleEn ?: null,
            'description' => $this->description ?: null,
            'description_en' => $this->descriptionEn ?: null,
            'type' => $this->type,
            'location_type' => $this->locationType,
            'facility_id' => $this->locationType === 'facility' ? $this->facilityId : null,
            'location_name' => $this->locationType === 'external' ? $this->locationName : null,
            'location_address' => $this->locationType === 'external' ? $this->locationAddress : null,
            'map_locations' => array_filter($this->mapLocations, fn ($loc) => !empty($loc['lat']) && !empty($loc['lng'])),
            'starts_at' => $this->startsAt,
            'ends_at' => $this->endsAt,
            'registration_opens_at' => $this->registrationOpensAt ?: null,
            'registration_closes_at' => $this->registrationClosesAt ?: null,
            'max_capacity' => $this->maxCapacity ?: null,
            'form_fields' => $this->formFields,
        ];

        try {
            if ($this->editing) {
                $event = $eventService->update($this->event, $data, auth()->user());
            } else {
                $event = $eventService->create($data, auth()->user());
            }

            if ($this->coverPhoto) {
                if ($event->cover) {
                    $mediaService->replace($event->cover, $this->coverPhoto);
                } else {
                    $mediaService->upload(
                        $this->coverPhoto,
                        MediaCollection::EventCover,
                        Event::class,
                        $event->id
                    );
                }
            }

            foreach ($this->galleryPhotos as $photo) {
                $mediaService->upload(
                    $photo,
                    MediaCollection::EventGallery,
                    Event::class,
                    $event->id
                );
            }

            $this->event = $event;
            $this->completed = true;
            session()->flash('success', $this->editing ? 'تم تحديث الحدث بنجاح' : 'تم إنشاء الحدث بنجاح');
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        } catch (\Exception $e) {
            session()->flash('error', 'حدث خطأ أثناء الحفظ: ' . $e->getMessage());
        }
    }

    private function sanitizeFormFields(): void
    {
        foreach ($this->formFields as $i => &$field) {
            $field['key'] = $field['key'] ?: 'field_' . ($i + 1);
            $field['key'] = preg_replace('/[^a-z0-9_]/', '_', strtolower($field['key']));

            $type = FormFieldType::tryFrom($field['type']);
            if (!$type || !$type->hasOptions()) {
                $field['options'] = [];
                $field['allow_other'] = false;
            }

            if (!$type || !$type->supportsUnique()) {
                $field['is_unique'] = false;
            }

            if ($type === FormFieldType::SectionDivider) {
                $field['is_required'] = false;
                $field['is_unique'] = false;
            }

            // Auto-fill option values from labels if empty
            foreach ($field['options'] as $oi => &$opt) {
                if (empty($opt['value']) && !empty($opt['label'])) {
                    $opt['value'] = preg_replace('/\s+/', '_', trim($opt['label']));
                }
            }

            // Remove empty options
            $field['options'] = array_values(array_filter($field['options'], fn ($o) => !empty($o['label'])));
        }
    }

    public function render()
    {
        return view('livewire.events.create-event-wizard', [
            'eventTypes' => EventType::cases(),
            'locationTypes' => LocationType::cases(),
            'fieldTypes' => FormFieldType::cases(),
            // Facility carries BranchScope, so this list is already limited to
            // the active branch — an explicit branch_id clause here would only
            // duplicate it.
            'facilities' => Facility::where('status', 'active')
                ->orderBy('name_ar')->get(['id', 'name_ar']),
        ]);
    }
}
