<?php

namespace App\Livewire\Environments\Supports;

use App\Livewire\Components\ConfirmModal;
use App\Models\Environments\Environment;
use App\Models\Monitoring\Log as MonitoringLog;
use App\Models\Support\EnvironmentSupport;
use App\Models\Support\Support;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\On;
use Livewire\Component;

/**
 * Los soportes contratados de un entorno: altas, cambios y bajas.
 *
 * **Esta pantalla no existía.** El catálogo de tipos de soporte (`/supports`) sí estaba
 * portado, y los permisos `admin.environments.supports.*` llevaban reservados en el
 * seeder desde el principio, pero **no había forma de asignar un soporte a un entorno**:
 * las 3 filas de `environment_support` en la base local venían de la importación del
 * Manager antiguo y la última acabó en marzo de 2023. Es el mismo patrón que el preview
 * de tutoriales y el borrado de logs: permiso y tablas sin punto de entrada.
 *
 * Sin esto, el aviso de soportes que caducan no tendría nunca de qué avisar.
 *
 * **El puntero de "soporte vigente" no se toca desde aquí**: lo recalcula el modelo en
 * cada guardado y borrado (ver `EnvironmentSupport::sincronizarEntorno()`). Un puntero
 * que hay que acordarse de actualizar en cada pantalla acaba en NULL.
 */
class Index extends Component
{
    public Environment $environment;

    /** Contrato que se está editando; null = alta nueva. */
    public ?int $editandoId = null;

    public ?int $supportId = null;
    public ?string $startAt = null;
    public ?string $endAt = null;
    public ?string $comment = null;

    public bool $mostrandoFormulario = false;

    public function mount(Environment $environment): void
    {
        $this->authorize('admin.environments.supports.index');

        $this->environment = $environment;
    }

    /* ==========================================================
       Formulario
       ========================================================== */

    public function nuevo(): void
    {
        $this->authorize('admin.environments.supports.create');

        $this->reset(['editandoId', 'supportId', 'startAt', 'endAt', 'comment']);
        $this->resetValidation();

        // Un año es la duración habitual de un contrato de soporte: se rellena por
        // comodidad y se puede cambiar. Escribir dos fechas a mano en cada alta es
        // donde se cuelan los errores de un dígito.
        $this->startAt = now()->format('Y-m-d');
        $this->endAt = now()->addYear()->format('Y-m-d');

        $this->mostrandoFormulario = true;
    }

    public function editar(int $id): void
    {
        $this->authorize('admin.environments.supports.edit');

        $contrato = $this->contratoDelEntorno($id);

        $this->editandoId = $contrato->id;
        $this->supportId = $contrato->support_id;
        $this->startAt = $contrato->start_at->format('Y-m-d');
        $this->endAt = $contrato->end_at->format('Y-m-d');
        $this->comment = $contrato->comment;

        $this->resetValidation();
        $this->mostrandoFormulario = true;
    }

    public function cancelar(): void
    {
        $this->reset(['editandoId', 'supportId', 'startAt', 'endAt', 'comment', 'mostrandoFormulario']);
        $this->resetValidation();
    }

    public function guardar(): void
    {
        $this->authorize($this->editandoId === null
            ? 'admin.environments.supports.create'
            : 'admin.environments.supports.edit');

        $datos = $this->validate([
            'supportId' => ['required', 'integer', 'exists:supports,id'],
            'startAt' => ['required', 'date'],
            // `after_or_equal` y no `after`: un soporte de un día es raro pero legítimo,
            // y al revés no significa nada.
            'endAt' => ['required', 'date', 'after_or_equal:startAt'],
            'comment' => ['nullable', 'string', 'max:255'],
        ], [], [
            'supportId' => 'tipo de soporte',
            'startAt' => 'fecha de inicio',
            'endAt' => 'fecha de fin',
            'comment' => 'comentario',
        ]);

        $contrato = $this->editandoId === null
            ? new EnvironmentSupport(['environment_id' => $this->environment->id])
            : $this->contratoDelEntorno($this->editandoId);

        $contrato->fill([
            'support_id' => $datos['supportId'],
            'start_at' => $datos['startAt'],
            'end_at' => $datos['endAt'],
            'comment' => $datos['comment'],
        ])->save();

        MonitoringLog::db(
            'info',
            '16015',
            ($this->editandoId === null ? 'Soporte asignado a' : 'Soporte modificado en')
            . ' ' . $this->environment->name . ': ' . ($contrato->support?->name ?? 'sin tipo')
            . ' del ' . $contrato->start_at->format('d/m/Y') . ' al ' . $contrato->end_at->format('d/m/Y')
            . ' (por ' . (Auth::user()?->name ?? 'desconocido') . ')',
            'EnvironmentSupport',
            (string) $contrato->id
        );

        $this->environment->refresh();
        $this->cancelar();

        session()->flash('success', 'Soporte guardado.');
    }

    /* ==========================================================
       Baja
       ========================================================== */

    public function pedirBaja(int $id): void
    {
        $this->authorize('admin.environments.supports.destroy');

        $contrato = $this->contratoDelEntorno($id);

        $this->dispatch('openModal',
            component: ConfirmModal::class,
            arguments: [
                'title' => 'Quitar el soporte',
                // Dice qué se pierde: el histórico de soporte es lo que responde a
                // "¿este cliente tenía soporte cuando pasó aquello?".
                'message' => 'Se va a borrar el soporte «' . ($contrato->support?->name ?? 'sin tipo')
                    . '» del ' . $contrato->start_at->format('d/m/Y') . ' al '
                    . $contrato->end_at->format('d/m/Y') . '. Desaparece del histórico del entorno '
                    . 'y no se puede deshacer. Si lo que ha pasado es que el contrato ha terminado, '
                    . 'no hace falta borrarlo: al caducar ya deja de contar.',
                'confirmText' => 'Sí, quitarlo',
                'cancelText' => 'Cancelar',
                'eventName' => 'environmentSupportDeleteConfirmed',
                'confirmButtonColor' => 'red',
                'itemId' => $contrato->id,
                'itemName' => $contrato->support?->name ?? 'el soporte',
            ]
        );
    }

    /**
     * OJO con la firma: `ConfirmModal` dispara el evento con un **array** de datos, así
     * que aquí no puede haber parámetros tipados ni inyección del contenedor. Ver
     * MGR-042.
     */
    #[On('environmentSupportDeleteConfirmed')]
    public function borrar($datos = null): void
    {
        $this->authorize('admin.environments.supports.destroy');

        $id = (int) (is_array($datos) ? ($datos['itemId'] ?? 0) : $datos);

        if ($id === 0) {
            return;
        }

        $contrato = $this->contratoDelEntorno($id);
        $descripcion = ($contrato->support?->name ?? 'sin tipo')
            . ' del ' . $contrato->start_at->format('d/m/Y') . ' al ' . $contrato->end_at->format('d/m/Y');

        $contrato->delete();

        MonitoringLog::db(
            'warning',
            '16016',
            'Soporte quitado de ' . $this->environment->name . ': ' . $descripcion
            . ' (por ' . (Auth::user()?->name ?? 'desconocido') . ')',
            'EnvironmentSupport',
            (string) $id
        );

        $this->environment->refresh();

        session()->flash('success', 'Soporte quitado.');
    }

    /**
     * Acota SIEMPRE al entorno de la URL.
     *
     * Sin esto, cambiando el id del formulario se podría editar o borrar el soporte de
     * **otro cliente** desde esta pantalla: los métodos viajan por `/livewire/update`,
     * donde el `permission:` de la ruta no se reaplica. Es MGR-005 y el IDOR de MGR-006.
     */
    private function contratoDelEntorno(int $id): EnvironmentSupport
    {
        return EnvironmentSupport::where('environment_id', $this->environment->id)
            ->findOrFail($id);
    }

    public function render()
    {
        return view('livewire.environments.supports.index', [
            'contratos' => EnvironmentSupport::with('support')
                ->where('environment_id', $this->environment->id)
                ->orderByDesc('end_at')
                ->get(),
            'tipos' => Support::orderBy('name')->get(),
            'vigenteId' => $this->environment->environment_support_id,
        ])->layout('layouts.app');
    }
}
