<?php

namespace App\Livewire\Setups;

use App\Services\Setups\RevisionDelSetup;
use App\Support\LimiteDeSubida;
use App\Models\Products\Product;
use App\Models\Setups\Setup;
use Livewire\Component;
use Livewire\WithFileUploads;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseException;

class Create extends Component
{
    use WithFileUploads;

    public Product $product;
    public string $version = '';
    public $yamlFile = null;
    public string $yaml = '';
    public ?string $description = null;
    public string $inputMethod = 'editor'; // 'editor' o 'file'
    public ?string $yamlValidationError = null;
    public bool $yamlValid = true;

    /**
     * Lo que la revisión estructural tiene que decir del setup.
     *
     * **No impide guardar.** Son avisos sobre cómo está escrito el fichero, no
     * sobre si el plugin lo va a aceptar — eso el Manager no lo sabe.
     *
     * @var array<int, string>
     */
    public array $avisosDelSetup = [];

    public function mount(Product $product)
    {
        $this->product = $product;
    }

    protected function rules()
    {
        $rules = [
            'version' => [
                'required',
                'string',
                'regex:/^\d{10}$/',
                function ($attribute, $value, $fail) {
                    // Validar que no exista un setup con el mismo producto y versión
                    $exists = Setup::where('product_id', $this->product->id)
                        ->where('version', $value)
                        ->exists();
                    
                    if ($exists) {
                        $fail('Ya existe un setup para este producto y versión.');
                    }
                },
            ],
            'description' => 'nullable|string',
        ];

        // Validación condicional según el método de entrada
        if ($this->inputMethod === 'file') {
            $rules['yamlFile'] = [
                'required',
                'file',
                'mimes:yaml,yml',
                'max:' . LimiteDeSubida::enKb(LimiteDeSubida::YAML_KB),
            ];
        } else {
            $rules['yaml'] = [
                'required',
                'string',
            ];
        }

        return $rules;
    }

    protected function messages()
    {
        return [
            'productId.required' => 'El producto es obligatorio.',
            'productId.exists' => 'El producto seleccionado no existe.',
            'version.required' => 'La versión es obligatoria.',
            'version.regex' => 'La versión debe tener el formato YYYYMMDDXX (10 dígitos).',
            'yamlFile.required' => 'El archivo YAML es obligatorio.',
            'yamlFile.mimes' => 'El archivo debe ser un archivo YAML (.yaml o .yml).',
            'yamlFile.max' => 'El archivo no puede ser mayor a 10MB.',
            'yaml.required' => 'El contenido YAML es obligatorio.',
        ];
    }

    /**
     * ¿Alguno de los avisos es de los que se arreglan poniendo bien la cabecera?
     *
     * Se decide por el texto del aviso y no por un campo aparte porque los avisos son
     * cadenas —así los devuelve la revisión— y añadirles estructura solo para esto obligaría
     * a tocar los siete tipos de aviso que hay.
     */
    public function cabeceraDescuadrada(): bool
    {
        foreach ($this->avisosDelSetup as $aviso) {
            if (str_contains($aviso, 'La cabecera dice') || str_contains($aviso, 'de la cabecera.')) {
                return true;
            }
        }

        return false;
    }

    /**
     * Pone en la cabecera el producto y la versión de verdad.
     *
     * **Rellenarla a mano es justo lo que se olvida**, y es de lo que avisa MGR-096: el caso
     * probable es copiar un setup de otro producto o de otra versión, cambiar el formulario y
     * dejar las dos primeras líneas del fichero diciendo lo anterior.
     *
     * No se hace solo al guardar: el fichero es de quien lo escribe, y reescribirle dos
     * líneas sin pedirlo es la clase de ayuda que hace desconfiar del editor.
     */
    public function rellenarCabecera(): void
    {
        // El gate de la ruta no se reaplica en /livewire/update (MGR-005).
        $this->authorize('admin.setups.create');

        if (trim($this->yaml) === '') {
            return;
        }

        $this->yaml = RevisionDelSetup::conLaCabeceraPuesta(
            $this->yaml,
            (string) $this->product->slug,
            (string) $this->version
        );

        // Y se vuelve a revisar, para que el aviso desaparezca en el momento: dejarlo puesto
        // después de haberlo arreglado hace dudar de si el botón ha hecho algo.
        $this->validateYaml();
    }

    public function validateYaml()
    {
        $this->yamlValidationError = null;
        $this->yamlValid = true;
        $this->avisosDelSetup = [];

        $yamlToValidate = $this->inputMethod === 'file' && $this->yamlFile 
            ? file_get_contents($this->yamlFile->getRealPath())
            : $this->yaml;

        if (empty(trim($yamlToValidate))) {
            $this->yamlValidationError = 'El YAML no puede estar vacío';
            $this->yamlValid = false;
            return;
        }

        try {
            $parsedData = Yaml::parse($yamlToValidate);

            if (!is_array($parsedData)) {
                $this->yamlValidationError = 'El YAML no contiene una estructura válida';
                $this->yamlValid = false;
                return;
            }

            $this->yamlValid = true;

            // **Revisión estructural, que no bloquea** (pedida el 2026-09-08). Dice si el
            // setup está **bien escrito** —claves conocidas, campos obligatorios,
            // referencias que resuelven—, y nunca si «va a funcionar»: eso solo lo sabe el
            // plugin, que es quien lo aplica. Ver `RevisionDelSetup`.
            $this->avisosDelSetup = RevisionDelSetup::revisar(
                $parsedData,
                // **El producto y la versión de la fila**, para que la revisión pueda
                // decir si la cabecera del fichero los contradice (MGR-096).
                $this->product->slug,
                (string) $this->version
            )['avisos'];
        } catch (ParseException $e) {
            $this->yamlValidationError = 'Error de sintaxis YAML: ' . $e->getMessage();
            $this->yamlValid = false;
        } catch (\Exception $e) {
            $this->yamlValidationError = 'Error al procesar YAML: ' . $e->getMessage();
            $this->yamlValid = false;
        }
    }

    public function updatedYamlFile()
    {
        if ($this->yamlFile) {
            $this->inputMethod = 'file';
            $content = file_get_contents($this->yamlFile->getRealPath());
            if ($content !== false) {
                $this->yaml = $content;
            }
        }
    }

    public function updatedInputMethod()
    {
        // Notificar al frontend del cambio
        $this->dispatch('inputMethodChanged', $this->inputMethod);
    }

    public function save()
    {
        $this->authorize('admin.setups.create');
        
        $validated = $this->validate();

        // Obtener contenido YAML según el método de entrada
        if ($this->inputMethod === 'file' && $this->yamlFile) {
            $yamlContent = file_get_contents($this->yamlFile->getRealPath());

            if ($yamlContent === false) {
                $this->addError('yamlFile', 'No se pudo leer el archivo YAML.');
                return;
            }
        } else {
            $yamlContent = $this->yaml;
        }

        // Validar YAML
        $this->validateYaml();

        if (!$this->yamlValid) {
            if ($this->inputMethod === 'file') {
                $this->addError('yamlFile', $this->yamlValidationError ?? 'YAML inválido');
            } else {
                $this->addError('yaml', $this->yamlValidationError ?? 'YAML inválido');
            }
            return;
        }

        // Guardar en BD
        Setup::create([
            'product_id' => $this->product->id,
            'version' => $validated['version'],
            'yaml' => $yamlContent,
            // **Borrador**, igual que los otros seis desde MGR-023. Era el único tipo
            // con estado y nacía publicado; dejarlo así sería tener dos flujos de
            // autoría distintos según el tipo de contenido.
            'status' => \App\Support\EstadoDePublicacion::BORRADOR,
            'description' => $validated['description'],
            'created_by' => auth()->id(),
        ]);

        session()->flash('success', 'Setup creado correctamente.');

        return $this->redirect(route('products.setups.index', $this->product), navigate: true);
    }

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


