<?php

namespace App\Livewire\LicenseTokens;

use App\Livewire\Traits\AssignsTokenEnvironments;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Products\LicenseToken;
use App\Models\Products\Product;
use App\Rules\UniqueEnvironmentDomain;
use Illuminate\Support\Facades\Auth;
use Livewire\Component;
use Livewire\WithPagination;

class CreateWizard extends Component
{
    use AssignsTokenEnvironments;
    use WithPagination;

    public int $currentStep = 1;
    public bool $autoGenerateToken = true;
    public ?string $token = null;
    public ?int $clientId = null;
    public ?string $name = null;
    public string $clientSearchQuery = '';
    public ?string $startAt = null;
    public ?string $endAt = null;
    public ?string $observation = null;
    public string $environmentMode = 'existing';
    public array $selectedEnvironmentIds = [];
    public array $newEnvironmentData = [];
    public array $selectedProductIds = [];
    public ?int $createdLicenseTokenId = null;

    protected $paginationTheme = 'tailwind';

    protected $listeners = [
        'modeChanged',
        'environmentsSelected',
        'newEnvironmentDataUpdated',
        'productsSelected',
    ];

    public function mount()
    {
        $this->currentStep = 1;
        $this->autoGenerateToken = true;
        $this->generateToken(); // Generar token automáticamente al inicio
        $this->newEnvironmentData = [
            'name' => '',
            'domain' => '',
            'description' => null,
            'version' => '',
            'env' => 'local',
            'type_id' => null,
        ];
    }

    public function updatedAutoGenerateToken()
    {
        if ($this->autoGenerateToken) {
            $this->generateToken();
        } else {
            $this->token = null;
        }
    }

    public function updatedClientId()
    {
        // Limpiar entornos seleccionados cuando cambia el cliente
        $this->selectedEnvironmentIds = [];
        $this->newEnvironmentData = [
            'name' => '',
            'domain' => '',
            'description' => null,
            'version' => '',
            'env' => 'local',
            'type_id' => null,
        ];
    }

    public function updatedClientSearchQuery()
    {
        // Resetear paginación cuando cambia la búsqueda
        $this->resetPage();
    }

    public function getClientsProperty()
    {
        $query = Client::query();

        if (!empty($this->clientSearchQuery)) {
            $query->where(function($q) {
                $q->where('name', 'like', '%' . $this->clientSearchQuery . '%')
                  ->orWhere('shortname', 'like', '%' . $this->clientSearchQuery . '%');
            });
        }

        return $query->orderBy('name')->paginate(10);
    }

    public function updatedStartAt()
    {
        // Auto-completar endAt = startAt + 1 año
        if ($this->startAt) {
            $startDate = \Carbon\Carbon::parse($this->startAt);
            $this->endAt = $startDate->addYear()->format('Y-m-d');
        }
    }

    public function modeChanged($mode)
    {
        $this->environmentMode = $mode;
    }

    public function environmentsSelected($ids)
    {
        $this->selectedEnvironmentIds = $ids;
    }

    public function newEnvironmentDataUpdated($data)
    {
        $this->newEnvironmentData = $data;
    }

    public function productsSelected($ids)
    {
        $this->selectedProductIds = $ids;
    }

    public function generateToken(): void
    {
        // El formato del token es contrato con el plugin y vive en un solo sitio
        // (MGR-022).
        $this->token = LicenseToken::generarToken();
    }

    public function nextStep()
    {
        // Validar según el step actual
        if ($this->currentStep === 1) {
            $this->validateStep1();

            // **Si el cliente no tiene ningún entorno, «entorno existente» no es una
            // opción.** Y no es cosmético: `validateStep2()` se ramifica por este modo,
            // así que dejándolo en `existing` el asistente exigía elegir uno de una
            // lista vacía y no había forma de pasar del paso 2. Se decide aquí, al
            // entrar en el paso, y no en el componente hijo: el modo lo guarda este
            // componente, y que lo cambie el hijo obliga a un evento de ida y vuelta
            // para que los dos estén de acuerdo.
            //
            // Se recalcula en cada paso de 1 a 2 a propósito: si se cambia el cliente
            // y se vuelve a avanzar, la respuesta puede ser otra.
            if ($this->clientId && Environment::where('client_id', $this->clientId)->count() === 0) {
                $this->environmentMode = 'new';
            }
        } elseif ($this->currentStep === 2) {
            $this->validateStep2();
        } elseif ($this->currentStep === 3) {
            $this->validateStep3();
        }

        if ($this->currentStep < 4) {
            $this->currentStep++;
        }
    }

    public function previousStep()
    {
        if ($this->currentStep > 1) {
            $this->currentStep--;
        }
    }

    protected function validateStep1()
    {
        $this->validate([
            'clientId' => 'required|exists:clients,id',
            'name' => 'nullable|string|max:255',
            'token' => 'required|string|max:100|unique:license_tokens,token',
            'startAt' => 'nullable|date',
            'endAt' => 'nullable|date|after_or_equal:startAt',
            'observation' => 'nullable|string',
        ], [], [
            'clientId' => 'cliente',
            'name' => 'nombre',
            'token' => 'token',
            'startAt' => 'fecha de inicio',
            'endAt' => 'fecha de fin',
        ]);
    }

    protected function validateStep2()
    {
        if ($this->environmentMode === 'existing') {
            $this->validate([
                'selectedEnvironmentIds' => 'required|array|min:1',
                'selectedEnvironmentIds.*' => 'exists:environments,id',
            ], [], [
                'selectedEnvironmentIds' => 'entornos',
            ]);
        } else {
            $this->validate([
                'newEnvironmentData.name' => 'required|string|max:255',
                'newEnvironmentData.domain' => ['required', 'string', 'max:255', new UniqueEnvironmentDomain($this->newEnvironmentData['env'] ?? null)],
                'newEnvironmentData.description' => 'nullable|string',
                'newEnvironmentData.version' => 'required|string|max:50',
                'newEnvironmentData.env' => 'required|string|in:local,develop,pre,pro',
                'newEnvironmentData.type_id' => 'nullable|exists:types,id',
            ], [], [
                'newEnvironmentData.name' => 'nombre',
                'newEnvironmentData.domain' => 'dominio',
                'newEnvironmentData.version' => 'versión',
                'newEnvironmentData.env' => 'desarrollo',
            ]);
        }
    }

    protected function validateStep3()
    {
        $this->validate([
            'selectedProductIds' => 'required|array|min:1',
            'selectedProductIds.*' => 'exists:products,id',
        ], [], [
            'selectedProductIds' => 'productos',
        ]);
    }

    public function saveLicense()
    {
        // Ver known-issues MGR-005: el gate de la ruta no basta como única defensa.
        $this->authorize('admin.license-tokens.create');

        // Validar todos los pasos
        $this->validateStep1();
        $this->validateStep2();
        $this->validateStep3();

        // Crear el entorno si es nuevo (justo antes de crear la licencia)
        if ($this->environmentMode === 'new' && !empty($this->newEnvironmentData['name'])) {
            // Validar datos del nuevo entorno
            $this->validate([
                'newEnvironmentData.name' => 'required|string|max:255',
                'newEnvironmentData.domain' => ['required', 'string', 'max:255', new UniqueEnvironmentDomain($this->newEnvironmentData['env'] ?? null)],
                'newEnvironmentData.description' => 'nullable|string',
                'newEnvironmentData.version' => 'required|string|max:50',
                'newEnvironmentData.env' => 'required|string|in:local,develop,pre,pro',
                'newEnvironmentData.type_id' => 'nullable|exists:types,id',
            ], [], [
                'newEnvironmentData.name' => 'nombre',
                'newEnvironmentData.domain' => 'dominio',
                'newEnvironmentData.version' => 'versión',
                'newEnvironmentData.env' => 'desarrollo',
            ]);

            $data = $this->newEnvironmentData;
            $data['client_id'] = $this->clientId;
            $data['active'] = true;
            $environment = Environment::create($data);
            $this->selectedEnvironmentIds = [$environment->id];
        }

        // Crear la licencia
        $licenseToken = LicenseToken::create([
            'client_id' => $this->clientId,
            'name' => $this->name,
            'token' => $this->token,
            'active' => true,
            'start_at' => $this->startAt ? \Carbon\Carbon::parse($this->startAt) : null,
            'end_at' => $this->endAt ? \Carbon\Carbon::parse($this->endAt) : null,
            'observation' => $this->observation,
            'created_by' => Auth::id(),
        ]);

        // Asociar productos
        if (!empty($this->selectedProductIds)) {
            $syncData = [];
            foreach ($this->selectedProductIds as $productId) {
                $syncData[$productId] = [
                    'status' => 'active',
                    'assigned_by' => Auth::id(),
                ];
            }
            $licenseToken->products()->sync($syncData);
        }

        // Asociar entornos, comprobando que son del mismo cliente que la licencia:
        // $selectedEnvironmentIds es una propiedad pública y llega del navegador, y
        // vincular el entorno de otro cliente se lo quita a su token, dejándolo sin
        // licencia. Ver known-issues MGR-004.
        if (!empty($this->selectedEnvironmentIds)) {
            $validIds = $this->validateAssignableEnvironments($licenseToken, $this->selectedEnvironmentIds);

            if ($validIds === null) {
                session()->flash('error', 'Alguno de los entornos seleccionados no pertenece a este cliente. La licencia se ha creado, pero sin entornos vinculados.');
            } else {
                $this->syncTokenEnvironments($licenseToken, $validIds);
            }
        }

        $this->createdLicenseTokenId = $licenseToken->id;
        $this->currentStep = 5;
    }

    public function render()
    {
        return view('livewire.license-tokens.create-wizard')->layout('layouts.clean');
    }
}

