<?php

namespace App\Models\Support;

use App\Models\Environments\Environment;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

/**
 * Un soporte contratado por un entorno, con sus fechas.
 *
 * La tabla `environment_support` estaba migrada desde el Manager antiguo y **solo se
 * usaba como pivote de un `belongsToMany`**: sin modelo, sin pantalla que la escribiera
 * y sin nada que consultara sus fechas. En la base local tenía 3 filas, la última
 * acabada en marzo de 2023, y `environments.environment_support_id` estaba a NULL en los
 * 7 entornos marcados con `has_support`.
 *
 * Tener modelo propio es lo que permite avisar de los soportes que caducan: el aviso
 * necesita recorrer contratos con fechas, no una relación de conveniencia.
 *
 * **`environments.environment_support_id` es un puntero al contrato vigente**, y es
 * derivable: es el de fecha de fin más lejana. Se recalcula solo (`sincronizarEntorno()`)
 * en cada alta, cambio o baja, porque un puntero que hay que acordarse de actualizar a
 * mano es un puntero que acaba en NULL — que es exactamente cómo estaba.
 */
class EnvironmentSupport extends Model
{
    protected $table = 'environment_support';

    protected $fillable = [
        'environment_id',
        'support_id',
        'comment',
        'start_at',
        'end_at',
    ];

    protected $casts = [
        'start_at' => 'date',
        'end_at' => 'date',
    ];

    public function environment(): BelongsTo
    {
        return $this->belongsTo(Environment::class);
    }

    public function support(): BelongsTo
    {
        return $this->belongsTo(Support::class);
    }

    /* ==========================================================
       Consultas
       ========================================================== */

    /** Contratos cuya fecha de fin cae en un día concreto. */
    public function scopeQueAcabanEl(Builder $query, \DateTimeInterface $dia): Builder
    {
        // `whereDate` y no una comparación directa: `end_at` es `date` en MariaDB y
        // TEXT en sqlite (los tests), y comparar contra un Carbon con hora no casa en
        // los dos motores. Misma trampa que en `DataAction`.
        return $query->whereDate('end_at', $dia->format('Y-m-d'));
    }

    public function scopeCaducados(Builder $query): Builder
    {
        return $query->whereDate('end_at', '<', now()->format('Y-m-d'));
    }

    /* ==========================================================
       Estado
       ========================================================== */

    /** Días que quedan; negativo si ya venció. Por días de calendario. */
    public function diasRestantes(): int
    {
        return (int) now()->startOfDay()->diffInDays($this->end_at->copy()->startOfDay(), false);
    }

    public function haCaducado(): bool
    {
        return $this->diasRestantes() < 1;
    }

    /** Uno de `Support::STATUS`. */
    public function estado(): int
    {
        return Support::get_status_support(
            $this->start_at->copy(),
            $this->end_at->copy()
        );
    }

    public function getEstadoTextoAttribute(): string
    {
        return Support::STATUS[$this->estado()] ?? 'Desconocido';
    }

    /* ==========================================================
       El puntero al contrato vigente
       ========================================================== */

    /**
     * Recalcula `environments.environment_support_id`.
     *
     * El vigente es **el de fin más lejano**, no el último creado: si alguien da de alta
     * hoy la renovación del año que viene y luego corrige un contrato viejo, el orden de
     * creación miente y el de fechas no.
     *
     * Si el entorno se queda sin contratos, el puntero vuelve a NULL y `has_support` a
     * false: dejar el `has_support` a true sin contrato es la incoherencia que ya
     * arrastraba la base.
     */
    public static function sincronizarEntorno(Environment $entorno): void
    {
        $vigente = self::where('environment_id', $entorno->id)
            ->orderByDesc('end_at')
            ->first();

        $entorno->update([
            'environment_support_id' => $vigente?->id,
            'has_support' => $vigente !== null,
        ]);
    }

    protected static function booted(): void
    {
        // Atado al modelo y no a la pantalla: el puntero tiene que quedar bien también
        // cuando lo toque un seeder, un comando o una importación.
        static::saved(fn (self $fila) => self::sincronizarEntorno($fila->environment));
        static::deleted(fn (self $fila) => self::sincronizarEntorno($fila->environment));
    }
}
