<?php

namespace App\Models\Monitoring;

use Illuminate\Database\Eloquent\Model;

/**
 * Un aviso automático ya enviado.
 *
 * Existe para **no repetir avisos**. Sin esto, una tarea diaria que avise de una
 * licencia a punto de caducar manda el mismo correo todos los días, y a los tres días
 * nadie lo lee.
 *
 * El truco está en `reference`: guarda el dato que motivó el aviso —para una licencia,
 * su fecha de fin—. Al renovar, la fecha cambia y los avisos viejos dejan de casar, así
 * que **renovar rearma el aviso por construcción**, sin observadores ni un `reset()`
 * que alguien pueda olvidar llamar.
 */
class Notice extends Model
{
    protected $table = 'notices';

    protected $fillable = [
        'subject_type',
        'subject_id',
        'kind',
        'threshold',
        'reference',
        'sent_at',
        'sent_to',
        'error',
        'payload',
    ];

    protected $casts = [
        'sent_at' => 'datetime',
        'threshold' => 'integer',
        'payload' => 'array',
    ];

    public const SUBJECT_LICENSE_TOKEN = 'license_token';
    public const SUBJECT_ENVIRONMENT_SUPPORT = 'environment_support';

    /** El sujeto de un aviso de corte es el episodio de `api_blocks`. */
    public const SUBJECT_API_BLOCK = 'api_block';

    /**
     * Cómo se llama cada tipo en pantalla.
     *
     * **En el modelo y no en la pantalla**, que es donde estaba: la lista vivía dentro del
     * `render()` de Avisos, y al hacer falta en la modal de limpieza lo que tocaba era
     * copiarla. Ya se había quedado sin el aviso de soporte por ese camino.
     */
    public const NOMBRES = [
        self::KIND_LICENCE_EXPIRING => 'Licencia por caducar',
        self::KIND_LICENCE_EXPIRED => 'Licencia caducada',
        self::KIND_SUPPORT_EXPIRING => 'Soporte por caducar',
        self::KIND_BLOCK_ALERT => 'Bloqueo de la API',
    ];

    public const KIND_LICENCE_EXPIRING = 'licence:expiring';
    public const KIND_LICENCE_EXPIRED = 'licence:expired';

    /**
     * Aviso de un soporte de entorno que caduca (o que ya caducó).
     *
     * **Un solo `kind` para los dos casos**, al contrario que en las licencias: aquí
     * hay varios umbrales (30/15/7/0 días) y el `threshold` ya los distingue. El 0 es
     * el aviso del día de la caducidad y en adelante.
     */
    public const KIND_SUPPORT_EXPIRING = 'support:expiring';

    /**
     * Aviso de un bloqueo nuevo de la API.
     *
     * **Faltaba, y la pantalla de Avisos prometía tenerlo.** El correo se mandaba y quedaba
     * en el log del panel, pero sin fila aquí: en Acciones del panel salía «Aviso de límite
     * de la API enviado» y en Avisos no había nada de eso. La pregunta de esa pantalla es
     * «¿se avisó de esto?», y justo del aviso que más corre no se podía contestar.
     *
     * `threshold` guarda el límite que se cruzó, que es lo que distingue dos avisos del
     * mismo sujeto por motivos distintos.
     */
    public const KIND_BLOCK_ALERT = 'block:alert';

    /**
     * ¿Ya se ha enviado este aviso?
     *
     * Solo cuenta como enviado si `sent_at` tiene valor: una fila con `error` y sin
     * fecha es un intento fallido, y ese sí se reintenta.
     */
    public static function yaEnviado(
        string $subjectType,
        int $subjectId,
        string $kind,
        int $threshold,
        string $reference
    ): bool {
        return self::where('subject_type', $subjectType)
            ->where('subject_id', $subjectId)
            ->where('kind', $kind)
            ->where('threshold', $threshold)
            ->where('reference', $reference)
            ->whereNotNull('sent_at')
            ->exists();
    }

    /**
     * Deja constancia de un aviso enviado (o de que falló).
     *
     * @param  array<int, string>  $destinatarios
     */
    public static function registrar(
        string $subjectType,
        int $subjectId,
        string $kind,
        int $threshold,
        string $reference,
        array $destinatarios,
        ?string $error = null,
        ?array $payload = null
    ): self {
        return self::updateOrCreate(
            [
                'subject_type' => $subjectType,
                'subject_id' => $subjectId,
                'kind' => $kind,
                'threshold' => $threshold,
                'reference' => $reference,
            ],
            [
                // Sin fecha si ha fallado: así el próximo pase lo reintenta.
                'sent_at' => $error === null ? now() : null,
                'sent_to' => implode(', ', $destinatarios),
                'error' => $error,
                'payload' => $payload,
            ]
        );
    }
}
