<?php

namespace App\Livewire\LicenseTokens;

use App\Models\Products\LicenseToken;
use App\Models\Products\Product;
use Illuminate\Support\Facades\Cache;
use Livewire\Component;
use Livewire\WithPagination;

/**
 * Listado de licencias.
 *
 * Implementa `Licencias.dc.html` del sistema de diseño (Claude Design).
 *
 * **Lo que había**: un buscador y una tabla, sin un solo filtro. La pregunta que trae
 * aquí a Comercial es «a quién le caduca en 30 días» y se respondía leyendo la tabla
 * entera. Y la tabla venía de un componente compartido con el asistente de alta, así
 * que no podía tener nada propio de este listado.
 *
 * Ahora la consulta y las filas viven aquí. `LicensesTable` sigue existiendo para la
 * pantalla de asociar licencia, que necesita otra fila —con radio de selección— y no
 * puede compartir esta.
 */
class Index extends Component
{
    use WithPagination;

    public string $search = '';

    /**
     * todas | activas | caducadas | apagadas.
     *
     * **Caducada y apagada son cosas distintas** aunque las dos corten el servicio: una
     * se renueva y la otra se vuelve a encender. Que estuvieran en la misma columna
     * «Estado» sin poder separarlas era el problema 2 del brief.
     */
    public string $estado = 'todas';

    /** Solo las que caducan en 30 días o menos, sin contar las ya caducadas. */
    public bool $soloPronto = false;

    /** Licencias vivas que ningún entorno usa: un contrato que nadie ha instalado. */
    public bool $soloSinEntornos = false;

    public ?int $producto = null;

    protected $queryString = [
        'search',
        'estado' => ['except' => 'todas'],
        'producto' => ['except' => null],
        'soloPronto' => ['except' => false],
        'soloSinEntornos' => ['except' => false],
    ];

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

    public function updatedSearch(): void
    {
        $this->dispatch('search-updated', search: $this->search);
    }

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

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

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

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

    public function limpiarFiltros(): void
    {
        $this->reset(['search', 'estado', 'producto', 'soloPronto', 'soloSinEntornos']);
        $this->resetPage();
    }

    public function hayFiltros(): bool
    {
        return $this->search !== ''
            || $this->estado !== 'todas'
            || $this->producto !== null
            || $this->soloPronto
            || $this->soloSinEntornos;
    }

    public function delete($id)
    {
        $this->authorize('admin.license-tokens.destroy');

        $token = LicenseToken::findOrFail($id);
        $token->delete();

        session()->flash('success', 'Token eliminado correctamente.');
    }

    /**
     * Los cuatro números de la cabecera.
     *
     * Cacheado 60 segundos: son cuatro consultas agregadas y `render()` se ejecuta en
     * cada tecla del buscador.
     *
     * @return array{total:int, caducadas:int, apagadas:int, pronto:int, sinUso:int}
     */
    private function resumen(): array
    {
        return Cache::remember('license_tokens:resumen', 60, function () {
            $hoy = now()->format('Y-m-d');

            return [
                'total' => LicenseToken::count(),
                // Caducada: activa pero con la fecha pasada. Si además está apagada
                // cuenta como apagada y no como las dos, o los números no suman.
                'caducadas' => LicenseToken::where('active', true)
                    ->whereNotNull('end_at')
                    ->whereDate('end_at', '<', $hoy)
                    ->count(),
                'apagadas' => LicenseToken::where('active', false)->count(),
                // Rango **cerrado**: de hoy a 30 días. Sin la cota inferior, una vencida
                // saldría también aquí y se contaría dos veces (MGR-025).
                'pronto' => LicenseToken::where('active', true)
                    ->whereNotNull('end_at')
                    ->whereDate('end_at', '>=', $hoy)
                    ->whereDate('end_at', '<=', now()->addDays(30)->format('Y-m-d'))
                    ->count(),
                'sinUso' => LicenseToken::where('active', true)
                    ->where(fn ($q) => $q->whereNull('end_at')->orWhereDate('end_at', '>=', $hoy))
                    ->whereDoesntHave('environments')
                    ->count(),
            ];
        });
    }

    public function render()
    {
        $hoy = now()->format('Y-m-d');

        $tokens = LicenseToken::query()
            ->with(['client', 'products'])
            ->withCount('environments')
            ->when($this->search !== '', function ($query) {
                // Los `orWhere` dentro de su propio paréntesis: si van sueltos, el
                // primer `AND` de cualquier otro filtro se los come y la lista deja de
                // respetar la pestaña. Es el error que apareció en Clientes (MGR-054).
                $query->where(function ($sub) {
                    $termino = '%' . $this->search . '%';

                    $sub->where('token', 'like', $termino)
                        ->orWhere('name', 'like', $termino)
                        ->orWhereHas('client', fn ($q) => $q->where('name', 'like', $termino))
                        // **Y por entorno**, que era lo que faltaba: la pregunta real de
                        // soporte es «¿qué licencia sirve a moodle51.dev?», y desde aquí no
                        // se podía responder. Se busca por nombre y por dominio porque
                        // quien pregunta tiene una de las dos cosas, no las dos.
                        ->orWhereHas('environments', fn ($q) => $q
                            ->where('name', 'like', $termino)
                            ->orWhere('domain', 'like', $termino));
                });
            })
            ->when($this->estado === 'activas', function ($query) use ($hoy) {
                $query->where('active', true)
                    ->where(fn ($q) => $q->whereNull('end_at')->orWhereDate('end_at', '>=', $hoy));
            })
            ->when($this->estado === 'caducadas', function ($query) use ($hoy) {
                $query->where('active', true)
                    ->whereNotNull('end_at')
                    ->whereDate('end_at', '<', $hoy);
            })
            ->when($this->estado === 'apagadas', fn ($query) => $query->where('active', false))
            ->when($this->soloPronto, function ($query) use ($hoy) {
                $query->where('active', true)
                    ->whereNotNull('end_at')
                    ->whereDate('end_at', '>=', $hoy)
                    ->whereDate('end_at', '<=', now()->addDays(30)->format('Y-m-d'));
            })
            ->when($this->soloSinEntornos, fn ($query) => $query->whereDoesntHave('environments'))
            ->when($this->producto !== null, function ($query) {
                // Por la relación y no por la tabla pivote a pelo: la relación filtra
                // los productos dados de baja en blando (`wherePivotNull`), y un
                // producto quitado no debería hacer que la licencia salga en su filtro.
                $query->whereHas('products', fn ($q) => $q->where('products.id', $this->producto));
            })
            // Las que cortan servicio primero, y dentro de esas las que caducan antes.
            // Un listado ordenado por `id` descendente pone arriba la última creada, que
            // es la que menos falta hace mirar.
            ->orderByRaw('case when active = 0 then 0 else 1 end')
            ->orderByRaw('case when end_at is null then 1 else 0 end')
            ->orderBy('end_at')
            ->paginate(20);

        return view('livewire.license-tokens.index', [
            'tokens' => $tokens,
            'resumen' => $this->resumen(),
            'productos' => Product::orderBy('name')->get(['id', 'name']),
            'hayFiltros' => $this->hayFiltros(),
        ])->layout('layouts.app');
    }
}
