<?php

namespace App\Models\Environments;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Plugin extends Model
{
    use HasFactory, SoftDeletes;

    protected $table = 'plugins';

    protected $fillable = [
        'environment_id',
        'name',
        'component',
        'type',
        // Si viene con Moodle. Nulo si el sitio no lo manda. Ver `leerEsEstandar()`.
        'isstandard',
        'versiondisk',
        'versiondb',
        'versionrequires',
        'pluginsupported',
        'pluginincompatible',
        'release',
        'has_dependencies',
        'dependencies',
        'has_updates',
        'availableupdates',
    ];

    /**
     * Lee el `isstandard` que manda el plugin, tal como llegue.
     *
     * **Nunca falla**: un valor raro no puede tumbar un `sync` —es el criterio de
     * `availableupdatesstatus`—, así que lo que no se entienda se guarda como `null`, «no se
     * sabe», que es lo mismo que un sitio que no lo manda. Se aceptan las formas en que un
     * booleano puede llegar en JSON desde PHP: `true`/`false`, `1`/`0` y sus cadenas.
     */
    public static function leerEsEstandar(mixed $valor): ?bool
    {
        if (is_bool($valor)) {
            return $valor;
        }

        if (is_int($valor) || is_string($valor)) {
            return match (strtolower(trim((string) $valor))) {
                '1', 'true' => true,
                '0', 'false' => false,
                default => null,
            };
        }

        return null;
    }

    protected $casts = [
        'isstandard' => 'boolean',
        'has_dependencies' => 'boolean',
        'has_updates' => 'boolean',
        'dependencies' => 'array',
        'availableupdates' => 'array',
    ];

    /**
     * Los que Moodle tiene registrados y **cuyos ficheros no están en el disco**.
     *
     * Es el «Missing from disk» de la pantalla de plugins de Moodle. La regla está aquí y no
     * dentro de cada pantalla porque la miran tres —el inventario de un entorno, el listado
     * de entornos y el parque—: si cada una la reescribiera, el día que cambie una dirían
     * cosas distintas y no habría forma de saber cuál miente.
     *
     * **La ausencia no llega como `null`.** Las dos columnas son `NOT NULL`, así que lo que
     * se guarda es el `'0'` que ponen las acciones de la API cuando el payload no trae el
     * dato; se aceptan también `''` y `null` porque hay filas antiguas de las dos maneras, y
     * ninguna marca `YYYYMMDDXX` real es cero.
     *
     * Y **se exige que la de base de datos sí esté**: sin ninguna de las dos no hay avería,
     * hay un payload incompleto, y afirmar que falta del disco sería inventarse el
     * diagnóstico.
     */
    public function scopeAusenteDelDisco($query)
    {
        return $query
            ->where(fn ($q) => $q->whereNull('versiondisk')->orWhereIn('versiondisk', ['', '0']))
            ->whereNotNull('versiondb')
            ->whereNotIn('versiondb', ['', '0']);
    }

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

