<?php

namespace App\Livewire\Facilities;

use App\Domain\Facility\Enums\FacilityStatus;
use App\Domain\Facility\Enums\FacilityType;
use App\Domain\Facility\Models\Facility;
use App\Domain\Facility\Services\FacilityService;
use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Shared\Traits\UsesBranchScope;
use Illuminate\Support\Collection;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Layout('layouts.app')]
#[Title('إدارة المنشأة')]
class FacilityForm extends Component
{
    use UsesBranchScope;

    /**
     * Route-model bound, so BranchScope resolves it — another branch's facility
     * 404s rather than opening. Locked so the browser cannot swap the id on a
     * later round-trip and point save() at a different facility.
     */
    #[Locked]
    public ?Facility $facility = null;

    public bool $editing = false;

    public string $name = '';
    public string $name_ar = '';
    public string $code = '';
    public string $type = 'field';
    public string $status = 'active';
    public string $description = '';
    public string $description_ar = '';
    public ?int $capacity = null;
    public string $surface_type = '';
    public ?string $area_sqm = null;
    public ?string $length_m = null;
    public ?string $width_m = null;
    public bool $is_indoor = false;
    public bool $has_lighting = true;
    public bool $has_ac = false;
    public string $operating_start = '';
    public string $operating_end = '';
    public ?string $rental_cost_display = null;
    public ?string $monthly_rental_cost_display = null;
    public string $address = '';
    public ?string $latitude = null;
    public ?string $longitude = null;
    public int $sort_order = 0;

    /** Sports this facility hosts. Empty = any sport (no filtering). */
    public array $activityIds = [];

    /** Live grid size, edited straight from facility settings. */
    public int $gridRows = 1;
    public int $gridColumns = 1;
    public ?string $gridError = null;
    public string $branch_id = '';

    public function mount(?Facility $facility = null): void
    {
        $this->authorize($facility?->exists ? 'facilities.update' : 'facilities.create');

        if ($facility && $facility->exists) {
            $this->facility = $facility;
            $this->editing = true;
            $this->name = $facility->name ?? '';
            $this->name_ar = $facility->name_ar;
            $this->code = $facility->code ?? '';
            $this->type = $facility->type->value ?? $facility->type;
            $this->status = $facility->status->value ?? $facility->status;
            $this->description = $facility->description ?? '';
            $this->description_ar = $facility->description_ar ?? '';
            $this->capacity = $facility->capacity;
            $this->surface_type = $facility->surface_type ?? '';
            $this->area_sqm = $facility->area_sqm;
            $this->length_m = $facility->length_m;
            $this->width_m = $facility->width_m;
            $this->is_indoor = $facility->is_indoor;
            $this->has_lighting = $facility->has_lighting;
            $this->has_ac = $facility->has_ac;
            $this->operating_start = $facility->operating_start ?? '';
            $this->operating_end = $facility->operating_end ?? '';
            $this->rental_cost_display = $facility->rental_cost_per_hour ? (string) ($facility->rental_cost_per_hour / 100) : null;
            $this->monthly_rental_cost_display = $facility->monthly_rental_cost ? (string) ($facility->monthly_rental_cost / 100) : null;
            $this->address = $facility->address ?? '';
            $this->latitude = $facility->latitude;
            $this->longitude = $facility->longitude;
            $this->sort_order = $facility->sort_order;
            $this->branch_id = (string) $facility->branch_id;

            $this->activityIds = $facility->activities()->pluck('activities.id')->all();

            $layout = $facility->activeLayout;
            $config = $layout?->layout_config ?? [];
            $this->gridRows = (int) ($config['rows'] ?? 1);
            $this->gridColumns = (int) ($config['columns'] ?? 1);
        } else {
            // A new facility belongs to the branch you are working in. The
            // picker only appears at all when the user is in all-branches mode.
            $this->branch_id = (string) ($this->getActiveBranchId() ?? '');
        }
    }

    /**
     * The branches this form may offer and accept.
     *
     * selectableBranches() returns exactly one entry unless the user is
     * genuinely in all-branches mode — the topbar switcher is the branch
     * selector, and a second one here is how a facility got moved into a branch
     * the user could not see, taking its layouts and bookings with it.
     *
     * The facility's own branch is kept valid while editing even if that branch
     * has since been deactivated, so an unrelated edit is still savable.
     */
    private function branchOptions(): Collection
    {
        $options = $this->selectableBranches();

        $current = $this->editing ? $this->facility?->branch : null;

        if ($current && ! $options->contains('id', $current->id)) {
            $options = $options->push($current);
        }

        return $options->values();
    }

    private function selectableBranchIds(): array
    {
        return $this->branchOptions()->pluck('id')->map(fn ($id) => (int) $id)->all();
    }

    public function rules(): array
    {
        return [
            'name' => 'nullable|string|max:255',
            'name_ar' => 'required|string|max:255',
            'code' => 'nullable|string|max:20',
            'type' => 'required|in:field,court,pool,gym,track,hall,room,outdoor,other',
            'status' => 'required|in:active,maintenance,closed,reserved,unavailable',
            'description' => 'nullable|string',
            'description_ar' => 'nullable|string',
            'capacity' => 'nullable|integer|min:1',
            'surface_type' => 'nullable|string|max:50',
            'area_sqm' => 'nullable|numeric|min:0',
            'length_m' => 'nullable|numeric|min:0',
            'width_m' => 'nullable|numeric|min:0',
            'is_indoor' => 'boolean',
            'has_lighting' => 'boolean',
            'has_ac' => 'boolean',
            'operating_start' => 'nullable|date_format:H:i',
            'operating_end' => 'nullable|date_format:H:i|after:operating_start',
            'rental_cost_display' => 'nullable|numeric|min:0',
            'monthly_rental_cost_display' => 'nullable|numeric|min:0',
            'address' => 'nullable|string|max:255',
            'latitude' => 'nullable|numeric|between:-90,90',
            'longitude' => 'nullable|numeric|between:-180,180',
            'sort_order' => 'integer|min:0',
            // exists: is a raw table query and would accept any branch in the
            // academy; branch_id is a plain public property, so that is a
            // cross-branch write, not just a bad option.
            'branch_id' => ['required', \Illuminate\Validation\Rule::in($this->selectableBranchIds())],
        ];
    }

    public function messages(): array
    {
        return [
            'name_ar.required' => 'اسم المنشأة بالعربية مطلوب',
            'name_ar.max' => 'اسم المنشأة يجب ألا يتجاوز 255 حرف',
            'type.required' => 'نوع المنشأة مطلوب',
            'type.in' => 'نوع المنشأة غير صالح',
            'status.required' => 'حالة المنشأة مطلوبة',
            'status.in' => 'حالة المنشأة غير صالحة',
            'capacity.integer' => 'السعة يجب أن تكون رقم صحيح',
            'capacity.min' => 'السعة يجب أن تكون 1 على الأقل',
            'area_sqm.numeric' => 'المساحة يجب أن تكون رقم',
            'area_sqm.min' => 'المساحة لا يمكن أن تكون سالبة',
            'length_m.numeric' => 'الطول يجب أن يكون رقم',
            'width_m.numeric' => 'العرض يجب أن يكون رقم',
            'operating_start.date_format' => 'وقت البداية يجب أن يكون بصيغة ساعة:دقيقة',
            'operating_end.date_format' => 'وقت النهاية يجب أن يكون بصيغة ساعة:دقيقة',
            'operating_end.after' => 'وقت النهاية يجب أن يكون بعد وقت البداية',
            'rental_cost_display.numeric' => 'تكلفة الإيجار بالساعة يجب أن تكون رقم',
            'rental_cost_display.min' => 'تكلفة الإيجار بالساعة لا يمكن أن تكون سالبة',
            'monthly_rental_cost_display.numeric' => 'تكلفة الإيجار الشهري يجب أن تكون رقم',
            'monthly_rental_cost_display.min' => 'تكلفة الإيجار الشهري لا يمكن أن تكون سالبة',
            'branch_id.required' => 'الفرع مطلوب',
            'branch_id.in' => 'الفرع غير متاح',
            'sort_order.integer' => 'الترتيب يجب أن يكون رقم صحيح',
            'sort_order.min' => 'الترتيب يجب أن يكون 0 أو أكثر',
            'latitude.between' => 'خط العرض يجب أن يكون بين -90 و 90',
            'longitude.between' => 'خط الطول يجب أن يكون بين -180 و 180',
        ];
    }

    public function save(FacilityService $service): void
    {
        $this->validate();

        try {
            $data = [
                'name' => $this->name ?: $this->name_ar,
                'name_ar' => $this->name_ar,
                'code' => $this->code ?: null,
                'type' => $this->type,
                'status' => $this->status,
                'description' => $this->description ?: null,
                'description_ar' => $this->description_ar ?: null,
                'capacity' => $this->capacity,
                'surface_type' => $this->surface_type ?: null,
                'area_sqm' => $this->area_sqm,
                'length_m' => $this->length_m,
                'width_m' => $this->width_m,
                'is_indoor' => $this->is_indoor,
                'has_lighting' => $this->has_lighting,
                'has_ac' => $this->has_ac,
                'operating_start' => $this->operating_start ?: null,
                'operating_end' => $this->operating_end ?: null,
                'rental_cost_per_hour' => $this->rental_cost_display ? (int) round((float) $this->rental_cost_display * 100) : null,
                'monthly_rental_cost' => $this->monthly_rental_cost_display ? (int) round((float) $this->monthly_rental_cost_display * 100) : null,
                'address' => $this->address ?: null,
                'latitude' => $this->latitude,
                'longitude' => $this->longitude,
                'sort_order' => $this->sort_order,
                'branch_id' => (int) $this->branch_id,
            ];

            if ($this->editing) {
                $service->update($this->facility, $data);
                $this->facility->activities()->sync($this->activityIds);
                $this->syncGrid();
                session()->flash('success', __('تم تحديث المنشأة بنجاح'));
            } else {
                $service->create(
                    $data,
                    auth()->user(),
                    $this->activityIds,
                    ['rows' => $this->gridRows, 'columns' => $this->gridColumns],
                );
                session()->flash('success', __('تم إنشاء المنشأة بنجاح'));
            }

            $this->redirect(route('facilities.list'), navigate: true);
        } catch (DomainException $e) {
            session()->flash('error', $e->getMessage());
        }
    }

    /**
     * Apply the grid size typed in facility settings.
     *
     * A facility with no layout gets one rather than staying grid-less — that
     * silent gap is why new facilities showed an empty schedule.
     */
    private function syncGrid(): void
    {
        $this->gridError = null;
        $layouts = app(\App\Domain\Facility\Services\SpaceLayoutService::class);

        $rows = max(1, $this->gridRows);
        $columns = max(1, $this->gridColumns);

        try {
            $layout = $this->facility->activeLayout;

            if (! $layout) {
                $layouts->createDefaultLayout($this->facility, auth()->user(), $rows, $columns);

                return;
            }

            $config = $layout->layout_config ?? [];
            if ((int) ($config['rows'] ?? 0) === $rows && (int) ($config['columns'] ?? 0) === $columns) {
                return;
            }

            $layouts->resizeGrid($layout, $rows, $columns);
        } catch (DomainException $e) {
            // Shrinking onto a booked segment is refused; keep the rest of the save.
            $this->gridError = $e->getMessage();
            session()->flash('warning', $e->getMessage());
        }
    }

    public function render()
    {
        return view('livewire.facilities.facility-form', [
            'branches' => $this->branchOptions(),
            'activities' => \App\Domain\Training\Models\Activity::where('is_active', true)
                ->orderBy('name_ar')
                ->get(['id', 'name_ar']),
            'typeOptions' => collect(FacilityType::cases())->mapWithKeys(fn ($t) => [$t->value => $t->label()]),
            'statusOptions' => collect(FacilityStatus::cases())->mapWithKeys(fn ($s) => [$s->value => $s->label()]),
        ]);
    }
}
