<?php

namespace App\Livewire\LicenseTokens;

use App\Livewire\Traits\AssignsTokenEnvironments;
use App\Models\Environments\Environment;
use App\Models\Products\LicenseToken;
use LivewireUI\Modal\ModalComponent;

/**
 * Asociar un entorno a una licencia.
 *
 * **Lo que había** era un «modo edición» que cambiaba media ficha: casillas sobre la
 * lista completa de entornos del cliente y un «Guardar cambios» que sincronizaba todo de
 * golpe. Con ese formato, quitar un entorno y añadir otro eran el mismo gesto y no se
 * veía qué iba a pasar.
 *
 * El diseño lo convierte en una modal: se elige **uno**, y antes de confirmar la lista
 * dice de cada candidato si está libre o **de qué licencia se lo vas a quitar**, que es
 * la consecuencia que nadie ve venir —un entorno apunta a **una sola** licencia—.
 *
 * La comprobación de pertenencia sigue en el trait `AssignsTokenEnvironments`: asociar un
 * entorno de otro cliente se lo quita a su token y lo deja sin licencia (MGR-004), y esa
 * regla no puede vivir en tres sitios distintos.
 */
class EnvironmentAssociationModal extends ModalComponent
{
    use AssignsTokenEnvironments;

    public LicenseToken $licenseToken;

    /** Filtro por dominio o nombre. Con muchos entornos, la lista sola no basta. */
    public string $busqueda = '';

    public ?int $elegido = null;

    public function mount(int $licenseTokenId): void
    {
        // El permiso de la ruta no se reaplica en /livewire/update. Ver MGR-005.
        $this->authorize('admin.license-tokens.manage-environments');

        $this->licenseToken = LicenseToken::with('client')->findOrFail($licenseTokenId);
    }

    public static function modalMaxWidth(): string
    {
        return '2xl';
    }

    /**
     * Los entornos del cliente que todavía no usan esta licencia.
     *
     * **Solo del mismo cliente**: es la misma regla que valida el trait al guardar, y
     * ofrecer aquí lo que después se va a rechazar no ayuda a nadie.
     *
     * @return \Illuminate\Support\Collection
     */
    public function candidatos()
    {
        if ($this->licenseToken->client_id === null) {
            return collect();
        }

        return Environment::query()
            ->with(['type', 'licenseToken:id,name,token'])
            ->where('client_id', $this->licenseToken->client_id)
            ->where(fn ($q) => $q->whereNull('license_token_id')
                ->orWhere('license_token_id', '!=', $this->licenseToken->id))
            ->when($this->busqueda !== '', function ($query) {
                $termino = '%' . $this->busqueda . '%';

                $query->where(fn ($sub) => $sub->where('domain', 'like', $termino)
                    ->orWhere('name', 'like', $termino));
            })
            // Los libres primero: son los que se asocian sin quitárselos a nadie.
            ->orderByRaw('case when license_token_id is null then 0 else 1 end')
            ->orderBy('domain')
            ->limit(50)
            ->get();
    }

    public function elegir(int $environmentId): void
    {
        $this->elegido = $this->elegido === $environmentId ? null : $environmentId;
    }

    public function asociar(): void
    {
        $this->authorize('admin.license-tokens.manage-environments');

        if ($this->elegido === null) {
            return;
        }

        // Por el trait: los IDs llegan del navegador y hay que revalidar que el entorno
        // es del cliente de este token (MGR-004).
        $actuales = $this->licenseToken->environments()->pluck('environments.id')->all();
        $validos = $this->validateAssignableEnvironments($this->licenseToken, [...$actuales, $this->elegido]);

        if ($validos === null) {
            session()->flash('error', 'La operación se ha cancelado: ese entorno no pertenece al cliente de esta licencia.');
            $this->closeModal();

            return;
        }

        $entorno = Environment::find($this->elegido);
        $licenciaAnterior = $entorno?->licenseToken;

        $this->syncTokenEnvironments($this->licenseToken, $validos);

        // Si se lo hemos quitado a otra licencia, se dice: es la consecuencia que no se
        // ve, y el sitio no cambia de comportamiento pero sí de contrato.
        if ($licenciaAnterior !== null) {
            session()->flash('warning', $entorno->domain . ' ha pasado de «'
                . ($licenciaAnterior->name ?: $licenciaAnterior->token) . '» a esta licencia.');
        } else {
            session()->flash('success', $entorno->domain . ' ya usa esta licencia.');
        }

        // Clave numérica: el paquete hace `[$event, $params] = $event` cuando el valor es
        // un array, así que `['evento' => []]` revienta con "Undefined array key 0".
        $this->closeModalWithEvents(['licenseEnvironmentsChanged']);
    }

    public function render()
    {
        return view('livewire.license-tokens.environment-association-modal', [
            'candidatos' => $this->candidatos(),
        ]);
    }
}
