<?php

namespace App\Livewire\Js;

use App\Models\Products\Product;
use App\Models\Js\JsFile;
use App\Models\Js\JsVersion;
use App\Services\Js\JsStorageService;
use App\Support\RevisionDeCodigo;
use Livewire\Component;

class Edit extends Component
{
    public Product $product;
    public JsVersion $jsVersion;
    public JsFile $jsFile;
    public string $filename = '';
    public string $content = '';
    public ?string $jsValidationError = null;
    public bool $jsValid = true;

    /**
     * Lo que ha encontrado la revisión del archivo.
     *
     * Ver {@see \App\Support\RevisionDeCodigo}: no compila nada —no hay compilador de
     * SASS ni motor de JavaScript en el servidor— pero mira lo que se puede mirar
     * leyendo el texto, que es lo que más veces rompe un archivo: algo sin cerrar, y un
     * `@import` que apunta a un archivo que no está en esta versión.
     *
     * @var array<int, array{tipo: string, mensaje: string, linea: ?int}>
     */
    public array $revision = [];

    public function mount(Product $product, JsVersion $jsVersion, JsFile $jsFile)
    {
        $this->product = $product;
        $this->jsVersion = $jsVersion;
        $this->jsFile = $jsFile->load(['jsVersion.product', 'creator', 'updater']);
        $this->filename = $jsFile->filename;
        $this->content = $jsFile->content;
    }

    /**
     * Revisa el archivo y deja el resultado en $revision.
     *
     * **Antes esto solo miraba si el contenido estaba vacío** y después la pantalla
     * pintaba «JS válido» en verde: un archivo con una llave sin cerrar pasaba con el
     * visto bueno puesto. Ahora se revisa lo que se puede revisar sin compilar, y el
     * resto lo marca el editor mientras se escribe.
     */
    public function validateJs()
    {
        $this->jsValidationError = null;
        $this->jsValid = true;
        $this->revision = [];

        // El archivo vacío entra en la misma lista que lo demás: si hay dos sitios
        // donde mirar lo que va mal, se mira uno.
        if (empty(trim($this->content))) {
            $this->jsValidationError = 'El archivo está vacío.';
            $this->jsValid = false;
            $this->revision = [[
                'tipo' => 'error',
                'mensaje' => 'El archivo está vacío: no hay nada que servirle al cliente.',
                'linea' => null,
            ]];

            return;
        }

        $this->revision = RevisionDeCodigo::revisar(
            $this->content,
            RevisionDeCodigo::JS,
            $this->hermanos()
        );

        $this->jsValid = ! RevisionDeCodigo::hayErrores($this->revision);

        if (! $this->jsValid) {
            $this->jsValidationError = 'El archivo tiene errores que impiden guardarlo.';
        }
    }

    /**
     * Los otros archivos de esta versión, para resolver los `@import`.
     *
     * Se excluye el que se está editando: importarse a sí mismo no es un caso que haya
     * que dar por bueno.
     *
     * @return array<int, string>
     */
    private function hermanos(): array
    {
        return JsFile::where('js_version_id', $this->jsFile->js_version_id)
            ->where('id', '!=', $this->jsFile->id)
            ->pluck('filename')
            ->all();
    }

    public function save()
    {
        $this->authorize('admin.js.file.edit');
        
        // Validar JS antes de guardar
        $this->validateJs();

        if (!$this->jsValid) {
            $this->addError('content', $this->jsValidationError ?? 'JS inválido');
            return;
        }

        // Validar que el nombre del archivo no haya cambiado o que no exista otro con ese nombre
        if ($this->filename !== $this->jsFile->filename) {
            $exists = JsFile::where('js_version_id', $this->jsFile->js_version_id)
                ->where('filename', $this->filename)
                ->where('id', '!=', $this->jsFile->id)
                ->exists();

            if ($exists) {
                $this->addError('filename', 'Ya existe un archivo con este nombre para esta versión.');
                return;
            }

            // Si cambió el nombre, eliminar el archivo físico antiguo
            JsStorageService::deleteFile(
                $this->product,
                $this->jsFile->jsVersion,
                $this->jsFile->filename
            );
        }

        // Actualizar en BD
        $this->jsFile->update([
            'filename' => $this->filename,
            'content' => $this->content,
            'updated_by' => auth()->id(),
        ]);

        // Actualizar archivo físico en storage
        $saved = JsStorageService::saveFile(
            $this->product,
            $this->jsFile->jsVersion,
            $this->filename,
            $this->content
        );

        if (!$saved) {
            $this->addError('content', 'Error al guardar el archivo físico.');
            return;
        }

        session()->flash('success', 'Archivo JS actualizado correctamente.');

        return $this->redirect(route('products.js.file.show', [$this->product, $this->jsVersion, $this->jsFile]), navigate: true);
    }

    public function render()
    {
        return view('livewire.js.edit', [
            'product' => $this->product,
        ])->layout('layouts.clean');
    }
}

