<?php

namespace App\Livewire\Clients;

use App\Models\Clients\Client;
use Livewire\Component;
use Livewire\WithPagination;

class Index extends Component
{
    use WithPagination;

    public string $search = '';

    /**
     * alta | baja | todos.
     *
     * Por defecto **alta**: el listado es la herramienta de trabajo del día a día, y
     * los clientes que se han ido solo estorban ahí. Pero tienen que poder
     * encontrarse, que es lo que no pasaba: no se veía el estado ni había forma de
     * filtrar por él.
     */
    public string $estado = 'alta';

    protected $queryString = ['search', 'estado' => ['except' => 'alta']];

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

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

    /**
     * Los tres números de la cabecera.
     *
     * `sinActividad` es el que da la medida del problema: en la base de desarrollo son
     * **18 de 20**, clientes dados de alta sin una sola licencia ni un solo entorno. Se
     * dice en la cabecera porque un listado que los pinta igual que a los demás hace
     * pensar que hay 20 clientes trabajando.
     *
     * @return array{total:int, alta:int, baja:int, sinActividad:int}
     */
    private function resumen(): array
    {
        $alta = Client::where('actived', true)->count();

        return [
            'total' => Client::count(),
            'alta' => $alta,
            'baja' => Client::where('actived', false)->count(),
            'sinActividad' => Client::where('actived', true)
                ->whereDoesntHave('tokens')
                ->whereDoesntHave('environments')
                ->count(),
        ];
    }

    /**
     * La consulta del listado, con sus filtros y su orden.
     *
     * **Una sola, para la pantalla y para la descarga** (PRODSECU-153): lo que se descarga es
     * exactamente lo que se ve, filtros incluidos, y no puede desincronizarse.
     */
    private function consulta(): \Illuminate\Database\Eloquent\Builder
    {
        return Client::query()
            ->with(['tokens', 'deactivatedBy'])
            // Los contadores van con subconsulta agregada y no iterando los modelos:
            // serializar un entorno cuesta 16 consultas (MGR-015) y aquí hay 15 filas
            // por página.
            ->withCount([
                'tokens',
                'environments',
                // Sin señal: **han** sincronizado y llevan 3 días o más sin hacerlo. El
                // `whereNotNull` es lo que separa la avería del alta sin terminar, y es
                // la misma definición que usa el listado de Entornos y el Dashboard: si
                // cada pantalla la calculara a su manera, dirían números distintos del
                // mismo parque.
                'environments as sin_senal_count' => fn ($q) => $q->where('active', true)
                    ->whereNotNull('refresh_at')
                    ->where('refresh_at', '<', now()->subDays(3)),
                'environments as sin_sincronizar_count' => fn ($q) => $q->where('active', true)
                    ->whereNull('refresh_at'),
            ])
            ->when($this->estado === 'alta', fn ($q) => $q->where('actived', true))
            ->when($this->estado === 'baja', fn ($q) => $q->where('actived', false))
            ->when($this->search !== '', function ($query) {
                // **El paréntesis es el arreglo de un error.** Los cinco `orWhere` iban
                // sueltos al mismo nivel que el `where('actived', …)` de la pestaña, y
                // `AND` liga más fuerte que `OR`: la condición acababa siendo
                // «(de alta Y coincide el nombre) O coincide el shortname O …», así que
                // buscando desde la pestaña «De alta» salían también los de baja.
                $query->where(function ($sub) {
                    $termino = '%' . $this->search . '%';

                    $sub->where('name', 'like', $termino)
                        ->orWhere('shortname', 'like', $termino)
                        ->orWhere('crm_id', 'like', $termino)
                        ->orWhere('cif', 'like', $termino)
                        ->orWhere('razon_social', 'like', $termino);
                });
            })
            // **Por actividad y no alfabético.** La pregunta que trae a esta pantalla es
            // «qué pasa con mis clientes», y con 18 de 20 vacíos el orden alfabético
            // entierra a los que tienen algo. Los entornos pesan doble porque son el
            // servicio en marcha; una licencia sin entorno es un contrato sin usar.
            // El nombre entra como segundo criterio para que el orden sea estable.
            ->orderByRaw('(environments_count * 2 + tokens_count) desc')
            ->orderBy('name');
    }

    /**
     * El listado en CSV, con los filtros que haya puestos (PRODSECU-153).
     *
     * **Lo que no sale, y por qué**: los **contactos** —nombres, correos y teléfonos de
     * personas: datos personales—, y los campos de texto libre —`observation`, `description`,
     * `metadata` y el motivo de la baja—, donde cualquiera puede haber pegado algo que no debe
     * salir del panel.
     */
    public function descargar(): \Symfony\Component\HttpFoundation\StreamedResponse
    {
        $this->authorize('admin.clients.index');

        $cabeceras = [
            'ID', 'Nombre', 'Nombre corto', 'ID CRM', 'CIF', 'Razón social', 'Inicio de la relación',
            'Estado', 'Fecha de baja', 'Licencias', 'Entornos',
            'Entornos sin señal', 'Entornos sin sincronizar', 'Alta en el Manager',
        ];

        $filas = $this->consulta()->get()->map(fn (Client $c) => [
            $c->id,
            $c->name,
            $c->shortname,
            $c->crm_id,
            $c->cif,
            $c->razon_social,
            $c->fecha_inicio_relacion?->format('Y-m-d'),
            $c->actived ? 'Alta' : 'Baja',
            $c->deactivated_at?->format('Y-m-d H:i'),
            $c->tokens_count,
            $c->environments_count,
            $c->sin_senal_count,
            $c->sin_sincronizar_count,
            $c->created_at?->format('Y-m-d'),
        ]);

        return \App\Support\ExportacionCsv::descargar('clientes', $cabeceras, $filas, [
            'estado' => $this->estado,
            'busqueda' => $this->search,
        ]);
    }

    public function render()
    {
        $resumen = $this->resumen();

        $clients = $this->consulta()->paginate(15);

        return view('livewire.clients.index', [
            'clients' => $clients,
            'resumen' => $resumen,
        ])->layout('layouts.app');
    }
}
