<?php

namespace App\Livewire\Layers;

use App\Models\Feature;
use App\Models\InspectionTemplate;
use App\Models\Layer;
use App\Models\Phase;
use App\Models\Project;
use App\Services\SpatialFileConverter;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Component;
use Livewire\WithFileUploads;

#[Layout('layouts.app')]
class LayerManager extends Component
{
    use WithFileUploads;

    public Project $project;

    public Phase $phase;

    public $layers;

    public $selectedLayer = null;

    public $visibleLayers = [];

    public $uploadFile = null;

    public $layerName = '';

    public $layerColor = '#3b82f6';

    // Batch assign
    public $templates = [];

    public $batchTemplateId = null;

    public $batchStatus = '';

    public function mount(Project $project, Phase $phase)
    {
        $this->project = $project;
        $this->phase = $phase;

        if ($this->phase->project_id !== $this->project->id) {
            abort(404);
        }

        $user = Auth::user();
        if (! $user->can('manage all') && ! $project->users()->where('user_id', $user->id)->exists()) {
            abort(403);
        }

        $this->templates = InspectionTemplate::where('project_id', $this->project->id)->get();
        $this->loadLayers();
        $this->visibleLayers = $this->layers->pluck('id')->toArray();
        $this->emitInitialLayersData();
    }

    // ── Data loaders ──────────────────────────────────────────────────────────

    public function loadLayers()
    {
        $this->layers = Layer::withCount('features')
            ->withAvg('features', 'progress')
            ->where('phase_id', $this->phase->id)
            ->latest()
            ->get();

        $this->visibleLayers = array_values(
            array_intersect($this->visibleLayers, $this->layers->pluck('id')->toArray())
        );
    }

    private function buildLayerPayload(Layer $layer): array
    {
        $color = $layer->color ?: '#3b82f6';
        $features = ($layer->relationLoaded('features') ? $layer->features : $layer->features()->get())
            ->map(fn ($f) => [
                'type' => 'Feature',
                'id' => $f->id,
                'geometry' => $f->geometry,
                'properties' => [
                    'name' => $f->name ?? 'Elemento',
                    'progress' => $f->progress,
                    'status' => $f->status ?? 'planned',
                    'responsible' => $f->responsible,
                    'template_id' => $f->template_id,
                ],
            ])->values()->toArray();

        return [
            'id' => $layer->id,
            'color' => $color,
            'geojson' => [
                'type' => 'FeatureCollection',
                'features' => $features,
                'style' => ['color' => $color],
            ],
        ];
    }

    private function emitInitialLayersData()
    {
        $this->layers->loadMissing('features');
        $this->dispatch('initialLayersData', [
            'layers' => $this->layers->map(fn ($l) => $this->buildLayerPayload($l)),
            'visibleLayers' => $this->visibleLayers,
            'selectedLayerId' => $this->selectedLayer?->id,
        ]);
    }

    // ── Visibility ────────────────────────────────────────────────────────────

    public function toggleLayerVisibility($layerId)
    {
        if ($this->selectedLayer && $this->selectedLayer->id == $layerId) {
            $this->dispatch('notify', 'No puedes ocultar la capa que estás editando');

            return;
        }
        if (in_array($layerId, $this->visibleLayers)) {
            $this->visibleLayers = array_values(array_diff($this->visibleLayers, [$layerId]));
        } else {
            $this->visibleLayers[] = $layerId;
        }
        $this->dispatch('visibilityChanged', $this->visibleLayers);
    }

    // ── Select ────────────────────────────────────────────────────────────────

    public function selectLayer($layerId)
    {
        $this->selectedLayer = Layer::with('features')->find($layerId);
        if (! $this->selectedLayer) {
            return;
        }

        if (! in_array($layerId, $this->visibleLayers)) {
            $this->visibleLayers[] = $layerId;
            $this->dispatch('visibilityChanged', $this->visibleLayers);
        }

        $payload = $this->buildLayerPayload($this->selectedLayer);
        $this->dispatch('layerSelectedForEdit', [
            'layerId' => $layerId,
            'geojson' => $payload['geojson'],
            'color' => $payload['color'],
        ]);
        $this->dispatch('notify', 'Editando: '.$this->selectedLayer->name);
    }

    // ── Import file ───────────────────────────────────────────────────────────

    public function importFile()
    {
        $user = Auth::user();
        if (! $user->can('upload layers')) {
            $this->dispatch('notify', 'Sin permisos para subir capas');

            return;
        }

        $this->validate([
            'uploadFile' => 'required|file|max:51200',
            'layerName' => 'required|string|max:255',
            'layerColor' => 'nullable|string|size:7',
        ]);

        $ext = strtolower($this->uploadFile->getClientOriginalExtension());
        $allowed = ['geojson', 'json', 'kmz', 'kml', 'shp', 'dwg', 'zip'];
        if (! in_array($ext, $allowed)) {
            $this->dispatch('notify', 'Extensión no permitida. Válidas: '.implode(', ', $allowed));

            return;
        }

        $geojson = SpatialFileConverter::convertToGeoJson($this->uploadFile);
        if (! $geojson) {
            $this->dispatch('notify', 'No se pudo convertir el archivo. Comprueba que sea GeoJSON, KML o Shapefile válido.');

            return;
        }

        $layerColor = $this->layerColor ?: '#3b82f6';
        $layerName = $this->layerName;

        try {
            DB::transaction(function () use ($geojson, $layerColor, $layerName, $user) {
                $path = $this->uploadFile->store(
                    "uploads/projects/{$this->project->id}/layers", 'public'
                );

                $layer = Layer::create([
                    'project_id' => $this->project->id,
                    'phase_id' => $this->phase->id,
                    'name' => $layerName,
                    'color' => $layerColor,
                    'original_file' => $path,
                    'uploaded_by' => $user->id,
                ]);

                $idx = 0;
                foreach ($geojson['features'] ?? [] as $fd) {
                    $idx++;
                    $name = trim($fd['properties']['name'] ?? '');
                    if ($name === '') {
                        $name = $layerName.' — Elemento '.$idx;
                    }

                    Feature::create([
                        'layer_id' => $layer->id,
                        'name' => $name,
                        'geometry' => $fd['geometry'],
                        'properties' => $fd['properties'] ?? [],
                        'template_id' => $fd['properties']['template_id'] ?? null,
                        'progress' => $fd['properties']['progress'] ?? 0,
                        'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
                                            ? $fd['properties']['status']
                                            : 'planned',
                        'responsible' => $fd['properties']['responsible'] ?? null,
                    ]);
                }

                $this->visibleLayers[] = $layer->id;
            });
        } catch (\Throwable $e) {
            $this->dispatch('notify', 'Error al importar: '.$e->getMessage());

            return;
        }

        $this->loadLayers();
        $this->reset(['uploadFile', 'layerName']);
        $this->emitInitialLayersData();
        $this->dispatch('notify', 'Capa importada correctamente');
    }

    // ── Create empty layer ────────────────────────────────────────────────────

    public function createEmptyLayer()
    {
        $user = Auth::user();
        if (! $user->can('upload layers')) {
            $this->dispatch('notify', 'Sin permisos para crear capas');

            return;
        }

        $layer = Layer::create([
            'project_id' => $this->project->id,
            'phase_id' => $this->phase->id,
            'name' => $this->layerName ?: 'Nueva capa',
            'color' => $this->layerColor ?: '#3b82f6',
            'original_file' => null,
            'uploaded_by' => $user->id,
        ]);

        $this->loadLayers();
        $this->visibleLayers[] = $layer->id;
        $this->selectLayer($layer->id);
        $this->emitInitialLayersData();
        $this->dispatch('notify', 'Capa vacía creada. Dibuja elementos en el mapa.');
    }

    // ── Save drawn GeoJSON ────────────────────────────────────────────────────

    #[On('save-manual-geojson')]
    public function saveManualGeojson($geojsonString)
    {
        if (! $this->selectedLayer) {
            $this->dispatch('notify', 'No hay capa seleccionada');

            return;
        }

        $geojson = json_decode($geojsonString, true);
        if (json_last_error() !== JSON_ERROR_NONE || ! isset($geojson['features'])) {
            $this->dispatch('notify', 'GeoJSON inválido');

            return;
        }

        $layerId = $this->selectedLayer->id;
        $layerName = $this->selectedLayer->name;

        try {
            DB::transaction(function () use ($geojson, $layerId, $layerName) {
                // forceDelete: reemplazamos completamente los elementos de la capa
                Feature::where('layer_id', $layerId)->forceDelete();

                $idx = 0;
                foreach ($geojson['features'] as $fd) {
                    $idx++;
                    $name = trim($fd['properties']['name'] ?? '');
                    if ($name === '') {
                        $name = $layerName.' — Elemento '.$idx;
                    }

                    Feature::create([
                        'layer_id' => $layerId,
                        'name' => $name,
                        'geometry' => $fd['geometry'],
                        'properties' => $fd['properties'] ?? [],
                        'template_id' => $fd['properties']['template_id'] ?? null,
                        'progress' => $fd['properties']['progress'] ?? 0,
                        'status' => in_array($fd['properties']['status'] ?? '', Feature::STATUSES)
                                            ? $fd['properties']['status']
                                            : 'planned',
                        'responsible' => $fd['properties']['responsible'] ?? null,
                    ]);
                }
            });
        } catch (\Throwable $e) {
            $this->dispatch('notify', 'Error al guardar: '.$e->getMessage());

            return;
        }

        $this->loadLayers();
        $this->selectLayer($this->selectedLayer->id);
        $this->emitInitialLayersData();
        $this->dispatch('notify', count($geojson['features']).' elementos guardados');
    }

    // ── Delete layer ──────────────────────────────────────────────────────────

    public function deleteLayer($layerId)
    {
        $user = Auth::user();
        if (! $user->can('delete layers')) {
            abort(403);
        }

        // Verify it belongs to this phase (prevents cross-project deletion)
        $layer = Layer::where('id', $layerId)->where('phase_id', $this->phase->id)->first();
        if (! $layer) {
            return;
        }

        if ($layer->original_file) {
            Storage::disk('public')->delete($layer->original_file);
        }
        $layer->features()->delete();
        $layer->delete();

        $this->loadLayers();
        if ($this->selectedLayer && $this->selectedLayer->id == $layerId) {
            $this->selectedLayer = null;
            $this->dispatch('layerSelectedForEdit', null);
        }
        $this->emitInitialLayersData();
        $this->dispatch('notify', 'Capa eliminada');
    }

    // ── Export GeoJSON ────────────────────────────────────────────────────────

    public function exportLayer($layerId)
    {
        $layer = Layer::with('features')
            ->where('id', $layerId)
            ->where('phase_id', $this->phase->id)
            ->first();
        if (! $layer) {
            return;
        }

        $fc = [
            'type' => 'FeatureCollection',
            'name' => $layer->name,
            'features' => $layer->features->map(fn ($f) => [
                'type' => 'Feature',
                'geometry' => $f->geometry,
                'properties' => array_merge($f->properties ?? [], [
                    'name' => $f->name,
                    'progress' => $f->progress,
                    'status' => $f->status,
                    'responsible' => $f->responsible,
                    'template_id' => $f->template_id,
                ]),
            ])->values()->toArray(),
        ];

        $filename = preg_replace('/[^a-z0-9_\-]/i', '_', $layer->name).'.geojson';

        return response()->streamDownload(function () use ($fc) {
            echo json_encode($fc, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        }, $filename, ['Content-Type' => 'application/geo+json']);
    }

    // ── Batch assign template / status ────────────────────────────────────────

    public function batchAssign($layerId)
    {
        $layer = Layer::where('id', $layerId)->where('phase_id', $this->phase->id)->first();
        if (! $layer) {
            return;
        }

        $data = [];
        if ($this->batchStatus && in_array($this->batchStatus, Feature::STATUSES)) {
            $data['status'] = $this->batchStatus;
        }
        if ($this->batchTemplateId) {
            $data['template_id'] = (int) $this->batchTemplateId;
        }
        if (empty($data)) {
            $this->dispatch('notify', 'Selecciona un estado o template para asignar');

            return;
        }

        $count = $layer->features()->update($data);
        $this->loadLayers();
        $this->emitInitialLayersData();
        $this->dispatch('notify', "$count elemento(s) actualizados");
    }

    // ── Cancel editing ────────────────────────────────────────────────────────

    public function cancelEditing()
    {
        $this->selectedLayer = null;
        $this->dispatch('layerSelectedForEdit', null);
    }

    public function render()
    {
        return view('livewire.layers.layer-manager');
    }
}
