<?php

namespace App\Models\Products;

use App\Models\Environments\Plugin;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;

/**
 * Un plugin nuestro, declarado a mano.
 *
 * **Es la lista de «esto es de Tresipunt»**, y su única razón de existir es que el Manager
 * pueda responder esa pregunta sin adivinarla por el nombre del plugin.
 *
 * No confundir con `Product`: un producto tiene ficha, licencias y contenido versionado.
 * Aquí solo hay un identificador. La mayoría de nuestros plugins no son productos
 * comerciales, y obligarlos a serlo para poder marcarlos era el problema.
 *
 * Los dos catálogos identifican, y se consultan juntos en `sonNuestros()`.
 */
class OwnPlugin extends Model
{
    use HasFactory;
    use SoftDeletes;

    protected $table = 'own_plugins';

    /**
     * El plugin orquestador: el que habla con la API del Manager.
     *
     * **No es un plugin nuestro más.** `local_tresipunt` es el que hace todas las
     * llamadas —licencias, contenido, telemetría, inventario—, así que si falta en un
     * entorno, o está a medio actualizar, **ese sitio deja de recibir licencias y
     * contenido y no envía nada**, y ninguna otra pantalla lo explicaría. Por eso se
     * destaca donde aparece.
     */
    public const ORQUESTADOR = 'local_tresipunt';

    protected $fillable = [
        'component',
        'name',
        'notes',
    ];

    /**
     * Cuánto se cachea la lista.
     *
     * La lee la pantalla de inventario de cada entorno y cambia muy poco —se toca cuando
     * sacamos un plugin nuevo—, así que no tiene sentido consultarla en cada render. Se
     * olvida al guardar o borrar, así que un cambio se nota en el momento.
     */
    private const CACHE_KEY = 'own-plugins:components';

    private const CACHE_SEGUNDOS = 600;

    /**
     * Los `component` que son nuestros: los declarados aquí **y** los del catálogo de
     * Productos.
     *
     * Los dos cuentan y ninguno manda sobre el otro: un producto es nuestro por
     * definición, y esta lista existe para los que no son productos.
     *
     * @return array<int, string>
     */
    public static function sonNuestros(): array
    {
        return Cache::remember(self::CACHE_KEY, self::CACHE_SEGUNDOS, function () {
            return self::query()
                ->pluck('component')
                ->merge(Product::query()->pluck('slug'))
                ->filter()
                ->unique()
                ->values()
                ->all();
        });
    }

    /** Se llama al guardar o borrar: si no, el cambio tarda diez minutos en notarse. */
    public static function olvidar(): void
    {
        Cache::forget(self::CACHE_KEY);
    }

    protected static function booted(): void
    {
        // Cualquier cambio en la lista invalida la caché, sin que quien lo haga tenga que
        // acordarse.
        static::saved(fn () => self::olvidar());
        static::deleted(fn () => self::olvidar());
        static::restored(fn () => self::olvidar());
    }

    /**
     * El producto del catálogo, si este plugin además es un producto.
     *
     * No es una relación de Eloquent porque la clave es el `component` contra el `slug`,
     * y declararla como `belongsTo` invitaría a `with()` sobre una columna que no es una
     * clave ajena.
     */
    public function producto(): ?Product
    {
        return Product::where('slug', $this->component)->first();
    }

    /** En cuántos entornos está instalado, según el último inventario de cada uno. */
    public function instalaciones(): int
    {
        return Plugin::where('component', $this->component)
            ->distinct()
            ->count('environment_id');
    }
}
