<?php

namespace App\Livewire\Products;

use App\Models\Monitoring\Log as MonitoringLog;
use App\Support\RepartoDeVersiones;
use App\Support\VersionBorrable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use LivewireUI\Modal\ModalComponent;

/**
 * Borrar un elemento de dentro de una versión (PRODSECU-198, segunda parte).
 *
 * **Lo que había era un `wire:confirm`**, o sea el `confirm()` del navegador, con todo el
 * mensaje metido en una línea:
 *
 *     Se borrará «X» de la versión 20260101, que ahora mismo reciben 3 entorno(s). ¿Seguir?
 *
 * Y tenía cuatro problemas, en orden de gravedad:
 *
 * 1. **El «3 entornos» era de la versión, no del elemento**, y eso no se distinguía: la
 *    frase se lee como «hay 3 sitios que van a perder esto», cuando en los tipos con estado
 *    solo lo pierden si además está **publicado**. Un borrador no lo recibe nadie y el
 *    mensaje avisaba igual.
 * 2. **No decía si estaba publicado**, que es lo único que determina si alguien lo está
 *    viendo.
 * 3. **No se puede deshacer** —ninguno de los modelos usa `SoftDeletes`— y no se decía.
 * 4. Un `confirm()` del navegador no se puede maquetar, algunos navegadores lo suprimen si
 *    se abusa, y no se puede probar en un test de Livewire.
 *
 * **Sirve para los cinco tipos**, y la diferencia que importa está en `VersionBorrable`:
 * Funcionalidades, Tutoriales y Recursos tienen estado —solo se sirve lo `published`—;
 * los archivos SCSS y JS **no**, así que todo lo que hay en la versión se sirve y borrar uno
 * lo quita de todos los sitios que la reciben.
 */
class BorrarElementoModal extends ModalComponent
{
    public string $tipo;

    public int $versionId;

    public int $elementoId;

    public function mount(string $tipo, int $versionId, int $elementoId): void
    {
        $ficha = VersionBorrable::de($tipo);

        // El permiso de la ruta no se reaplica en /livewire/update. Ver MGR-005.
        $this->authorize($ficha['permisoElemento']);

        $this->tipo = $tipo;
        $this->versionId = $ficha['modelo']::findOrFail($versionId)->id;

        // Acotado a esta versión en la consulta, no comparando después: si no es de aquí,
        // es un 404 (MGR-006).
        $this->elementoId = $ficha['modeloElemento']::where($ficha['claveDeVersion'], $this->versionId)
            ->findOrFail($elementoId)
            ->id;
    }

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

    /** @return array<string, mixed> */
    public function ficha(): array
    {
        return VersionBorrable::de($this->tipo);
    }

    public function version(): Model
    {
        return $this->ficha()['modelo']::with('product')->findOrFail($this->versionId);
    }

    public function elemento(): Model
    {
        return $this->ficha()['modeloElemento']::findOrFail($this->elementoId);
    }

    /**
     * Cómo se llama esto en pantalla.
     *
     * Los cinco tipos usan campos distintos: `title` en los tres de contenido, `name` en
     * los archivos. Se prueba en orden y se cae al id, que siempre está.
     */
    public function nombre(): string
    {
        $elemento = $this->elemento();

        foreach (['title', 'name', 'filename'] as $campo) {
            if (! empty($elemento->{$campo})) {
                return (string) $elemento->{$campo};
            }
        }

        return '#' . $elemento->id;
    }

    /**
     * ¿Cuántos entornos están viendo **este elemento** ahora mismo?
     *
     * **La corrección respecto al `confirm()` que había**: en los tipos con estado, solo
     * cuenta si está `published`. Uno en borrador o archivado no se sirve, así que da igual
     * a cuántos entornos les llegue la versión —no lo están viendo—, y avisar de ellos era
     * decir algo que no era verdad.
     */
    public function loVen(): int
    {
        $ficha = $this->ficha();

        if ($ficha['conEstado'] && $this->elemento()->status !== 'published') {
            return 0;
        }

        $version = $this->version();
        $producto = $version->product;

        if ($producto === null) {
            return 0;
        }

        // **Solo las que se sirven**: una versión en borrador o retirada no sale por la API
        // y no le está llegando a nadie (MGR-023).
        $publicadas = $ficha['modelo']::where('product_id', $producto->id)->active()->pluck('version');

        $reparto = RepartoDeVersiones::reparto($producto, $publicadas);

        return $reparto['porVersion'][(string) $version->version] ?? 0;
    }

    public function borrar(): void
    {
        $ficha = $this->ficha();

        $this->authorize($ficha['permisoElemento']);

        $elemento = $this->elemento();
        $version = $this->version();

        $nombre = $this->nombre();
        $estado = $ficha['conEstado'] ? (string) $elemento->status : null;
        $loVen = $this->loVen();

        $elemento->delete();

        // Se registra, que antes no se hacía: borrar contenido que se está sirviendo tiene
        // que dejar rastro de quién y de qué se llevó.
        MonitoringLog::db(
            'warning',
            '16035',
            ucfirst($ficha['elemento']) . ' borrado: «' . $nombre . '» de la versión '
            . $version->version
            . ' (' . ($version->product->name ?? 'producto desconocido') . ')'
            . ($estado !== null ? ' — estaba en ' . $estado : '')
            . ($loVen > 0 ? ', lo recibían ' . $loVen . ' entorno(s)' : '')
            . ' (por ' . (Auth::user()?->name ?? 'desconocido') . ')',
            class_basename($ficha['modeloElemento']),
            (string) $elemento->id
        );

        session()->flash(
            'success',
            '«' . $nombre . '» borrado de la versión ' . $version->version . '.'
            . ($loVen > 0
                ? ' Los ' . $loVen . ' entorno(s) que lo recibían dejarán de verlo en su siguiente petición.'
                : '')
        );

        // **`skipRender` no es una optimización, es obligatorio.** Después de borrar,
        // Livewire vuelve a renderizar el componente, y `render()` busca el modelo con
        // `findOrFail`: sin esto, borrar lanza un `ModelNotFoundException` justo después de
        // haber borrado bien. La modal se está cerrando, así que no hay nada que pintar.
        $this->skipRender();
        $this->closeModal();

        // **Sin esto la pantalla de detrás no se repinta**, y la fila borrada se queda a la
        // vista: parece que el botón no ha hecho nada. Lo escucha el trait de la pantalla.
        $this->dispatch('contenidoBorrado');
    }

    public function render()
    {
        return view('livewire.products.borrar-elemento-modal', [
            'ficha' => $this->ficha(),
            'version' => $this->version(),
            'elemento' => $this->elemento(),
            'nombre' => $this->nombre(),
            'loVen' => $this->loVen(),
        ]);
    }
}
