<?php

namespace App\Livewire\Sites;

use App\Enums\SiteType;
use App\Livewire\Concerns\HasCatalogToast;
use App\Livewire\Forms\SiteForm;
use App\Models\Department;
use App\Models\District;
use App\Models\Project;
use App\Models\Province;
use App\Models\Site;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\Support\Js;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
use Livewire\WithPagination;
use Throwable;

#[Layout('layouts.app', ['title' => 'Sitios', 'header' => 'Sitios'])]
#[Title('Sitios')]
class SiteIndex extends Component
{
    use HasCatalogToast;
    use WithPagination;

    public SiteForm $form;

    public string $search = '';

    public string $projectFilter = '';

    public int $perPage = 10;

    public bool $showModal = false;

    public bool $isEditing = false;

    public ?int $siteId = null;

    public bool $showDeleteConfirm = false;

    public ?int $deleteSiteId = null;

    public ?int $pickerDepartmentId = null;

    public ?int $pickerProvinceId = null;

    public bool $showMapModal = false;

    public function mount(): void
    {
        $this->authorize('viewAny', Site::class);
    }

    /** @return LengthAwarePaginator<int, Site> */
    #[Computed]
    public function sites(): LengthAwarePaginator
    {
        return Site::query()
            ->with(['district.province.department', 'project'])
            ->when($this->search !== '', function ($query) {
                $term = '%'.$this->search.'%';
                $query->where(function ($q) use ($term) {
                    $q->where('code', 'like', $term)
                        ->orWhere('name', 'like', $term);
                });
            })
            ->when($this->projectFilter !== '', function ($query) {
                if ($this->projectFilter === 'none') {
                    $query->whereNull('project_id');
                } else {
                    $query->where('project_id', (int) $this->projectFilter);
                }
            })
            ->withCount('works')
            ->orderBy('code')
            ->paginate($this->perPage);
    }

    #[Computed]
    public function totalSites(): int
    {
        return Site::query()->count();
    }

    #[Computed]
    public function activeSites(): int
    {
        return Site::query()->where('status', true)->count();
    }

    #[Computed]
    public function sitesWithProject(): int
    {
        return Site::query()->whereNotNull('project_id')->count();
    }

    /** @return Collection<int, Department> */
    #[Computed]
    public function departmentsForPicker(): Collection
    {
        return Department::query()->orderBy('name')->get();
    }

    /** @return Collection<int, Province> */
    #[Computed]
    public function provincesForPicker(): Collection
    {
        if ($this->pickerDepartmentId === null) {
            return collect();
        }

        return Province::query()
            ->where('department_id', $this->pickerDepartmentId)
            ->orderBy('name')
            ->get();
    }

    /** @return Collection<int, District> */
    #[Computed]
    public function districtsForPicker(): Collection
    {
        if ($this->pickerProvinceId === null) {
            return collect();
        }

        return District::query()
            ->where('province_id', $this->pickerProvinceId)
            ->orderBy('name')
            ->get();
    }

    /** @return Collection<int, Project> */
    #[Computed]
    public function projectsForSelect(): Collection
    {
        return Project::query()->orderBy('name')->get();
    }

    public function updatedPickerDepartmentId(): void
    {
        $this->pickerProvinceId = null;
        $this->form->district_id = null;
    }

    public function updatedPickerProvinceId(): void
    {
        $this->form->district_id = null;
    }

    public function openCreate(): void
    {
        $this->authorize('create', Site::class);
        $this->isEditing = false;
        $this->siteId = null;
        $this->pickerDepartmentId = null;
        $this->pickerProvinceId = null;
        $this->form->resetForCreate();
        $this->resetErrorBag();
        $this->showModal = true;
    }

    public function openEdit(int $id): void
    {
        $site = Site::query()->with(['district.province.department'])->find($id);

        if (! $site) {
            $this->showToastMessage('Sitio no encontrado.', 'error');

            return;
        }

        $this->authorize('update', $site);
        $this->siteId = $site->id;
        $this->isEditing = true;
        $this->form->fillFromModel($site);
        $this->pickerDepartmentId = $site->district->province->department_id;
        $this->pickerProvinceId = $site->district->province_id;
        $this->resetErrorBag();
        $this->showModal = true;
    }

    public function save(): void
    {
        if ($this->isEditing) {
            $site = Site::query()->findOrFail($this->siteId);
            $this->authorize('update', $site);
        } else {
            $this->authorize('create', Site::class);
        }

        $this->form->validate(
            $this->form->rules($this->isEditing ? $this->siteId : null),
            $this->form->validationMessages(),
        );

        try {
            $payload = [
                'code' => trim($this->form->code),
                'name' => trim($this->form->name),
                'latitude' => $this->form->latitude,
                'longitude' => $this->form->longitude,
                'maintenance_center' => trim($this->form->maintenance_center) !== '' ? trim($this->form->maintenance_center) : null,
                'type' => $this->form->type,
                'status' => $this->form->status,
                'district_id' => $this->form->district_id,
                'project_id' => $this->form->project_id ?: null,
            ];

            if ($this->isEditing) {
                $site = Site::query()->findOrFail($this->siteId);
                $site->update($payload);
                $message = 'Sitio actualizado correctamente.';
            } else {
                Site::query()->create($payload);
                $message = 'Sitio registrado correctamente.';
            }

            $this->closeModal();
            $this->resetPage();
            $this->showToastMessage($message, 'success');
        } catch (Throwable $e) {
            report($e);
            $this->showToastMessage('No se pudo guardar el sitio.', 'error');
        }
    }

    public function confirmDelete(int $id): void
    {
        $site = Site::query()->withCount('works')->find($id);

        if (! $site) {
            $this->showToastMessage('Sitio no encontrado.', 'error');

            return;
        }

        if ($site->works_count > 0) {
            $this->showToastMessage('No se puede eliminar: el sitio tiene trabajos asociados.', 'warning');

            return;
        }

        $this->authorize('delete', $site);
        $this->deleteSiteId = $id;
        $this->showDeleteConfirm = true;
    }

    public function destroy(): void
    {
        if ($this->deleteSiteId === null) {
            return;
        }

        $site = Site::query()->find($this->deleteSiteId);

        if (! $site) {
            $this->closeDeleteModal();
            $this->showToastMessage('Sitio no encontrado.', 'error');

            return;
        }

        $this->authorize('delete', $site);

        if ($site->works()->count() > 0) {
            $this->closeDeleteModal();
            $this->showToastMessage('No se puede eliminar: el sitio está en uso.', 'warning');

            return;
        }

        $site->delete();
        $this->closeDeleteModal();
        $this->resetPage();
        $this->showToastMessage('Sitio eliminado correctamente.', 'success');
    }

    public function closeModal(): void
    {
        $this->showModal = false;
        $this->isEditing = false;
        $this->siteId = null;
        $this->pickerDepartmentId = null;
        $this->pickerProvinceId = null;
        $this->form->resetForCreate();
        $this->resetErrorBag();
    }

    public function closeDeleteModal(): void
    {
        $this->showDeleteConfirm = false;
        $this->deleteSiteId = null;
    }

    public function resetSearch(): void
    {
        $this->search = '';
        $this->projectFilter = '';
        $this->resetPage();
    }

    public function updatedSearch(): void
    {
        $this->resetPage();
    }

    public function updatedProjectFilter(): void
    {
        $this->resetPage();
    }

    public function updatedPerPage(): void
    {
        $this->resetPage();
    }

    /** @return list<array{value: string, label: string}> */
    #[Computed]
    public function siteTypeOptions(): array
    {
        $options = [];
        foreach (SiteType::cases() as $case) {
            $options[] = ['value' => $case->value, 'label' => $case->label()];
        }

        return $options;
    }

    public function openSitesMap(): void
    {
        $this->authorize('viewAny', Site::class);

        $sites = Site::query()
            ->with(['project:id,name,color'])
            ->get()
            ->map(fn (Site $s) => [
                'lat' => (float) $s->latitude,
                'lng' => (float) $s->longitude,
                'code' => $s->code,
                'name' => $s->name,
                'project' => $s->project?->name,
                'color' => $s->project?->color
                    ? strtoupper((string) $s->project->color)
                    : Project::DEFAULT_MAP_COLOR,
            ])
            ->values()
            ->all();

        $this->showMapModal = true;
        $this->js('setTimeout(function(){ if (window.__setelRenderSitesMap) window.__setelRenderSitesMap('.Js::from($sites).'); }, 250)');
    }

    public function closeSitesMap(): void
    {
        $this->showMapModal = false;
        $this->js('if (window.__setelDestroySitesMap) window.__setelDestroySitesMap()');
    }

    public function render(): View
    {
        return view('livewire.sites.index');
    }
}
