<?php

namespace App\Livewire\Scss;

use App\Models\Products\Product;
use App\Models\Scss\ScssFile;
use App\Models\Scss\ScssVersion;
use App\Services\Scss\ScssStorageService;
use App\Livewire\Traits\HasFileValidation;
use App\Support\RevisionDeCodigo;
use Livewire\Component;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;

class Create extends Component
{
    use HasFileValidation;
    public Product $product;
    public ScssVersion $scssVersion;
    public string $version = '';
    public ?string $description = null;
    public array $files = [];
    public array $fileContents = []; // Contenido de cada archivo indexado por índice

    /**
     * Lo que ha encontrado la revisión, por índice de archivo.
     *
     * Ver {@see \App\Support\RevisionDeCodigo}. Va por índice y no en un montón
     * común porque cada aviso lleva un número de línea, y una línea sin saber de qué
     * archivo no sirve de nada.
     *
     * @var array<int, array<int, array{tipo: string, mensaje: string, linea: ?int}>>
     */
    public array $revision = [];

    public function mount(Product $product, ScssVersion $scssVersion)
    {
        $this->product = $product;
        $this->scssVersion = $scssVersion;

        // Verificar que la versión pertenezca al producto
        if ($this->scssVersion->product_id !== $this->product->id) {
            abort(404, 'La versión SCSS no pertenece a este producto.');
        }

        $this->version = $this->scssVersion->version;
        $this->description = $this->scssVersion->description;

        // Inicializar con un archivo vacío
        if (empty($this->files)) {
            $this->addFile();
        }
    }

    public function addFile()
    {
        // Sincronizar fileContents con files antes de añadir uno nuevo
        foreach ($this->fileContents as $idx => $content) {
            if (isset($this->files[$idx])) {
                $this->files[$idx]['content'] = $content ?? '';
            }
        }
        
        $index = count($this->files);
        $this->files[] = [
            'filename' => '',
            'content' => '',
        ];
        $this->fileContents[] = '';
    }

    public function removeFile($index)
    {
        if (isset($this->files[$index])) {
            unset($this->files[$index]);
            $this->files = array_values($this->files); // Reindexar array

            if (isset($this->fileContents[$index])) {
                unset($this->fileContents[$index]);
                $this->fileContents = array_values($this->fileContents); // Reindexar array
            }
        }

        // Si no quedan archivos, agregar uno vacío
        if (empty($this->files)) {
            $this->addFile();
        }
    }

    public function updatedFileContents($value, $index)
    {
        if (isset($this->files[$index])) {
            $this->files[$index]['content'] = $value;
        }
    }

    public function updatedFiles($value, $key)
    {
        // Asegurar que fileContents esté sincronizado
        $parts = explode('.', $key);
        if (count($parts) === 2 && $parts[1] === 'content') {
            $index = (int) $parts[0];
            if (isset($this->fileContents[$index])) {
                $this->fileContents[$index] = $value;
            }
        }
    }

    protected function getFileModel(): string
    {
        return ScssFile::class;
    }

    protected function getVersionId(): ?int
    {
        return $this->scssVersion->id ?? null;
    }

    protected function getFileExtension(): string
    {
        return 'scss';
    }

    protected function getVersionColumnName(): string
    {
        return 'scss_version_id';
    }

    protected function rules()
    {
        return $this->getFileValidationRules();
    }

    protected function messages()
    {
        return $this->getFileValidationMessages();
    }


    /**
     * Revisa los archivos del formulario y deja el resultado en $revision.
     *
     * **Los hermanos son dos cosas**: los que ya están en la versión y los otros del mismo
     * formulario. Se suben varios de una vez, y es normal escribir el que importa antes de
     * haber guardado el importado: avisar de eso sería un aviso falso.
     */
    private function revisarLosArchivos(): void
    {
        $this->revision = [];

        $enLaVersion = ScssFile::where('scss_version_id', $this->scssVersion->id)->pluck('filename')->all();
        $enElFormulario = array_filter(array_column($this->files, 'filename'));
        $hermanos = array_values(array_unique(array_merge($enLaVersion, $enElFormulario)));

        foreach ($this->files as $indice => $archivo) {
            $contenido = $archivo['content'] ?? ($this->fileContents[$indice] ?? '');

            if (trim((string) $contenido) === '') {
                continue;
            }

            $avisos = RevisionDeCodigo::revisar(
                (string) $contenido,
                RevisionDeCodigo::SCSS,
                $hermanos
            );

            if ($avisos !== []) {
                $this->revision[$indice] = $avisos;
            }
        }
    }

    /** ¿Alguno de los archivos tiene algo que impida guardarlo? */
    private function hayErroresEnLaRevision(): bool
    {
        foreach ($this->revision as $avisos) {
            if (RevisionDeCodigo::hayErrores($avisos)) {
                return true;
            }
        }

        return false;
    }

    public function save()
    {
        $this->authorize('admin.scss.file.create');
        
        // Se acepta lo que escribe la gente: `variables.scss` es un nombre válido y la
        // extensión se quita aquí, en vez de rechazarlo con un error que no la menciona.
        $this->normalizarNombres();

        // Sincronizar fileContents con files antes de validar
        foreach ($this->fileContents as $index => $content) {
            if (isset($this->files[$index])) {
                $this->files[$index]['content'] = $content ?? '';
            }
        }

        // Asegurar que todos los archivos tengan contenido (incluso si está vacío)
        foreach ($this->files as $index => &$file) {
            if (!isset($file['content']) || $file['content'] === null) {
                $file['content'] = $this->fileContents[$index] ?? '';
            }
        }
        unset($file); // Liberar referencia

        // La revisión de cada archivo, antes de escribir nada. Lo que encuentra no siempre
        // frena: un desbalance sí —ese archivo no va a compilar—, un `@import` a un archivo
        // que todavía no está solo se dice, porque puede ser el siguiente que se suba.
        $this->revisarLosArchivos();

        if ($this->hayErroresEnLaRevision()) {
            return;
        }

        // **Sin `try`.** Aquí había un `catch (ValidationException) { return; }` con el
        // comentario «los errores se mostrarán automáticamente», y es al revés: Livewire
        // llena el saco de errores cuando la excepción **sale** del método. Atrapándola, el
        // saco se quedaba vacío y la pantalla no decía nada: se pulsaba «Guardar archivos»,
        // no se guardaba, y no aparecía ningún mensaje en ningún sitio.
        $this->validate();

        // Usar la versión existente (siempre debe existir)
        $scssVersion = $this->scssVersion;

        $savedCount = 0;
        $errors = [];

        foreach ($this->files as $index => $file) {
            try {
                // Guardar en base de datos
                $scssFile = ScssFile::create([
                    'scss_version_id' => $scssVersion->id,
                    'filename' => $file['filename'],
                    'content' => $file['content'],
                    'created_by' => auth()->id(),
                ]);

                // Guardar físicamente en storage
                $saved = ScssStorageService::saveFile(
                    $this->product,
                    $scssVersion,
                    $file['filename'],
                    $file['content']
                );

                if (!$saved) {
                    // Si falla el guardado físico, eliminar de BD
                    $scssFile->delete();
                    $errors[] = "Error al guardar el archivo físico: {$file['filename']}";
                } else {
                    $savedCount++;
                }
            } catch (\Exception $e) {
                $errors[] = "Error al guardar {$file['filename']}: " . $e->getMessage();
            }
        }

        // Si no se guardó ningún archivo y es una versión nueva, eliminar la versión
        if ($savedCount === 0 && !$this->scssVersion) {
            $scssVersion->delete();
            session()->flash('error', 'No se pudo guardar ningún archivo. La versión no se creó.');
            return;
        }

        if ($savedCount > 0) {
            $message = $this->scssVersion
                ? "Se agregaron {$savedCount} archivo(s) SCSS correctamente."
                : "Se guardaron {$savedCount} archivo(s) SCSS correctamente.";
            session()->flash('success', $message);
            
            // Redirigir a la versión solo si se guardó al menos un archivo
            return $this->redirect(route('products.scss.version.show', [$this->product, $scssVersion]), navigate: true);
        }

        if (!empty($errors)) {
            session()->flash('error', implode('<br>', $errors));
        } else {
            // Si no se guardó nada y no hay errores específicos, mostrar error genérico
            session()->flash('error', 'No se pudo guardar ningún archivo. Por favor, verifica que todos los campos estén completos.');
        }
    }

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