<?php

namespace App\Livewire\Traits;

trait HasFileValidation
{
    /**
     * Obtiene el modelo de archivo a usar para validar duplicados (ej: ScssFile::class)
     */
    abstract protected function getFileModel(): string;

    /**
     * Obtiene el ID de la versión para validar duplicados
     */
    abstract protected function getVersionId(): ?int;

    /**
     * La extensión que lleva este tipo de archivo: `scss` o `js`.
     *
     * **No se guarda en el nombre**: la pone la capa de almacenamiento al construir la ruta.
     * Hace falta aquí para quitarla si el usuario la escribe, que es lo que hace cualquiera
     * en un campo que pide el nombre de un archivo.
     */
    abstract protected function getFileExtension(): string;

    /**
     * Deja el nombre como se guarda: sin espacios alrededor y sin la extensión.
     *
     * **Antes esto era un error de validación.** Escribir `variables.scss` —lo natural— se
     * rechazaba con «solo puede contener letras, números, guiones y guiones bajos», que no
     * dice nada del punto ni de que la extensión sobra. Y el sitio que escribe el archivo en
     * disco ya quitaba esa extensión si venía puesta: se estaba rechazando algo que el
     * sistema sabe tratar.
     */
    protected function normalizarNombreDeArchivo(?string $valor): string
    {
        $nombre = trim((string) $valor);
        $extension = '.' . ltrim($this->getFileExtension(), '.');

        if (str_ends_with(strtolower($nombre), $extension)) {
            $nombre = substr($nombre, 0, -strlen($extension));
        }

        return $nombre;
    }

    /** Pasa por {@see normalizarNombreDeArchivo()} todos los nombres del formulario. */
    protected function normalizarNombres(): void
    {
        foreach ($this->files as $indice => $archivo) {
            $this->files[$indice]['filename'] = $this->normalizarNombreDeArchivo($archivo['filename'] ?? '');
        }
    }

    /**
     * Obtiene las reglas de validación base para archivos
     */
    protected function getFileValidationRules(): array
    {
        $rules = [];

        // Reglas para archivos (siempre requeridas)
        $rules['files'] = [
            'required',
            'array',
            'min:1',
        ];
        
        $rules['files.*.filename'] = [
            'required',
            'string',
            'max:255',
            function ($attribute, $value, $fail) {
                // **El patrón es el que es, y el aviso tiene que decir cuál.** Decía «solo
                // puede contener letras, números, guiones y guiones bajos» mientras
                // rechazaba `botón` y `diseño`: las letras con acento y la eñe no entran en
                // `a-zA-Z`, y quien lo leía no entendía qué había hecho mal. El nombre acaba
                // siendo una ruta en disco y una clave en la API, así que el patrón se queda
                // estrecho a propósito; lo que cambia es que ahora se explica.
                if (!preg_match('/^[a-zA-Z0-9_-]+$/', (string) $value)) {
                    $fail(
                        'El nombre solo admite letras sin acentos (a-z), números, guiones y guiones bajos: '
                        . 'nada de espacios, acentos, eñes ni carpetas. La extensión no hace falta, se pone sola.'
                    );
                }
            },
        ];
        
        $rules['files.*.content'] = [
            'required',
            'string',
        ];

        // Validar que no existan duplicados (mismo nombre dentro de la versión)
        $rules['files.*.filename'][] = function ($attribute, $value, $fail) {
            // Extraer el índice correctamente: "files.1.filename" -> 1
            $parts = explode('.', $attribute);
            $index = isset($parts[1]) && is_numeric($parts[1]) ? (int) $parts[1] : -1;

            if ($value && $index >= 0) {
                // Verificar duplicados dentro del mismo formulario
                foreach ($this->files as $i => $file) {
                    if ($i !== $index && isset($file['filename']) && !empty($file['filename']) && $file['filename'] === $value) {
                        $fail('Ya existe otro archivo con el mismo nombre en este formulario.');
                        return;
                    }
                }

                // Verificar duplicados en la BD si hay una versión existente
                $versionId = $this->getVersionId();
                if ($versionId) {
                    $fileModel = $this->getFileModel();
                    $versionColumn = $this->getVersionColumnName();
                    
                    $exists = $fileModel::where($versionColumn, $versionId)
                        ->where('filename', $value)
                        ->exists();
                    if ($exists) {
                        $fail('Ya existe un archivo con este nombre en esta versión.');
                    }
                }
            }
        };

        return $rules;
    }

    /**
     * Obtiene el nombre de la columna de versión (ej: 'scss_version_id', 'js_version_id')
     */
    abstract protected function getVersionColumnName(): string;

    /**
     * Obtiene los mensajes de validación para archivos
     */
    protected function getFileValidationMessages(): array
    {
        return [
            'files.required' => 'Debe agregar al menos un archivo.',
            'files.min' => 'Debe agregar al menos un archivo.',
            'files.*.filename.required' => 'El nombre del archivo es obligatorio.',
            'files.*.filename.max' => 'El nombre del archivo no puede exceder 255 caracteres.',
            'files.*.content.required' => 'El contenido del archivo es obligatorio.',
        ];
    }
}

