<?php

namespace App\Livewire\Monitoring;

use App\Models\Monitoring\Log as MonitoringLog;
use App\Models\Monitoring\Notice;
use App\Services\System\LimpiezaDelPanel;
use Illuminate\Support\Carbon;
use LivewireUI\Modal\ModalComponent;

/**
 * Limpieza del histórico de avisos (`notices`).
 *
 * **No es la misma operación que limpiar cortes, aunque lo parezca.** Allí borrar pierde
 * histórico; aquí borrar **provoca correos**. Esta tabla existe para no repetir avisos: cada
 * fila dice «de esto ya se avisó», y se reconoce por su `reference`. Sin la fila, la tarea
 * diaria vuelve a encontrar la licencia a punto de caducar y manda el correo otra vez.
 *
 * Por eso la modal cuenta dos cosas distintas: **cuántas filas se van** y **cuántos de esos
 * avisos son recurrentes**, que son los que una tarea reevalúa cada día. Con cero de los
 * segundos, esto es limpieza y ya está; con veinte, son veinte correos que alguien va a
 * recibir mañana por la mañana.
 *
 * Lo que sí sobra casi siempre: los avisos **que fallaron**. Sin `sent_at` y con `error`, no
 * llegaron a nadie — y una vez arreglado el buzón de destino son ruido puro.
 */
class LimpiezaDeAvisosModal extends ModalComponent
{
    public string $desde = '';

    public string $hasta = '';

    /** Opcional: uno de los `kind` de {@see Notice}. */
    public string $tipo = '';

    /** `todos`, `sent` o `failed`. */
    public string $estado = 'todos';

    public function mount(): void
    {
        // MGR-005: el permiso de la ruta no se reaplica en /livewire/update, y mirar la
        // pantalla no es lo mismo que vaciarla.
        $this->authorize('admin.configs.edit');

        $this->desde = now()->subYear()->format('Y-m-d');
        $this->hasta = now()->subDays(30)->format('Y-m-d');
    }

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

    protected function rules(): array
    {
        return [
            'desde' => ['required', 'date'],
            'hasta' => ['required', 'date', 'after_or_equal:desde'],
            'tipo' => ['nullable', 'string', 'max:64'],
            'estado' => ['required', 'in:todos,sent,failed'],
        ];
    }

    protected function messages(): array
    {
        return [
            'hasta.after_or_equal' => 'La fecha final no puede ser anterior a la inicial.',
        ];
    }

    private function criterios(): ?array
    {
        try {
            $desde = Carbon::parse($this->desde)->startOfDay();
            $hasta = Carbon::parse($this->hasta)->endOfDay();
        } catch (\Throwable) {
            return null;
        }

        if ($hasta->lt($desde)) {
            return null;
        }

        return [
            'desde' => $desde,
            'hasta' => $hasta,
            'tipo' => $this->tipo !== '' ? $this->tipo : null,
            'estado' => $this->estado !== 'todos' ? $this->estado : null,
        ];
    }

    /**
     * Qué se lleva, y qué va a volver a mandarse.
     *
     * @return array{filas: int, seRearman: int, fallidos: int}
     */
    public function impacto(): array
    {
        $criterios = $this->criterios();

        if ($criterios === null) {
            return ['filas' => 0, 'seRearman' => 0, 'fallidos' => 0];
        }

        return [
            'filas' => LimpiezaDelPanel::contarAvisos($criterios),
            'seRearman' => LimpiezaDelPanel::contarAvisosQueSeRearman($criterios),
            // Los que no llegaron a nadie: el subconjunto que casi siempre sobra.
            'fallidos' => (clone LimpiezaDelPanel::avisos($criterios))->whereNull('sent_at')->count(),
        ];
    }

    /** Los tipos que de verdad hay, no el catálogo entero. */
    public function tipos(): array
    {
        return Notice::query()->distinct()->orderBy('kind')->pluck('kind')->all();
    }

    public function borrar(): void
    {
        $this->authorize('admin.configs.edit');
        $this->validate();

        $criterios = $this->criterios();

        if ($criterios === null) {
            return;
        }

        $rearmados = LimpiezaDelPanel::contarAvisosQueSeRearman($criterios);
        $borrados = LimpiezaDelPanel::borrarAvisos($criterios);

        MonitoringLog::db(
            'warning',
            '16042',
            $borrados . ' avisos borrados del histórico ('
                . 'del ' . $criterios['desde']->format('d/m/Y')
                . ' al ' . $criterios['hasta']->format('d/m/Y')
                . ($criterios['tipo'] !== null ? ', tipo ' . $criterios['tipo'] : '')
                . ($criterios['estado'] !== null ? ', ' . $criterios['estado'] : '')
                . '). ' . $rearmados . ' eran recurrentes y pueden volver a enviarse',
            'notices',
            null
        );

        $this->closeModal();

        $this->dispatch('avisosLimpiados');

        session()->flash('success', $borrados === 0
            ? 'No había ningún aviso que encajara con esos criterios.'
            : $borrados . ' avisos borrados.'
                . ($rearmados > 0
                    ? ' Ojo: ' . $rearmados . ' eran recurrentes, así que si la condición sigue '
                        . 'cumpliéndose volverán a enviarse.'
                    : ''));
    }

    public function render()
    {
        return view('livewire.monitoring.limpieza-de-avisos-modal', [
            'impacto' => $this->impacto(),
            'tipos' => $this->tipos(),
        ]);
    }
}
