<?php

namespace App\Models\Setups;

use App\Models\Auth\User;
use App\Models\Products\Product;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Setup extends Model
{
    use HasFactory;

    protected $table = 'setups';

    protected $fillable = [
        'product_id',
        'version',
        'yaml',
        'status',
        'description',
        'created_by',
        'change_log',
        'updated_by',
    ];

    protected $casts = [
        'status' => 'string',
    ];

    /* -----------------------------
       Relaciones
    ------------------------------*/

    /**
     * Producto al que pertenece el setup
     */
    public function product()
    {
        return $this->belongsTo(Product::class);
    }

    /**
     * Usuario que creó el setup
     */
    public function creator()
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Usuario que editó por última vez el setup
     */
    public function updater()
    {
        return $this->belongsTo(User::class, 'updated_by');
    }

    /**
     * Historial de cambios del setup
     */
    public function history()
    {
        return $this->hasMany(SetupHistory::class)->orderByDesc('created_at');
    }

    /* -----------------------------
       Scopes
    ------------------------------*/

    /**
     * Scope para filtrar setups activos
     */
    public function scopeActive($query)
    {
        return $query->where('status', 'active');
    }

    /**
     * Scope para filtrar setups deprecados
     */
    public function scopeDeprecated($query)
    {
        return $query->where('status', 'deprecated');
    }

    /**
     * Scope para filtrar por producto
     */
    public function scopeForProduct($query, $product)
    {
        $productId = $product instanceof Product ? $product->id : $product;
        return $query->where('product_id', $productId);
    }

    /* -----------------------------
       Métodos estáticos
    ------------------------------*/

    /**
     * Encuentra la versión compatible de setup para un producto y versión dados
     *
     * @param Product|int $product Producto o ID del producto
     * @param string $productVersion Versión del producto (formato YYYYMMDDXX)
     * @return Setup|null
     */
    public static function findCompatibleVersion($product, string $productVersion): ?Setup
    {
        $productId = $product instanceof Product ? $product->id : $product;
        $productVersionInt = (int) $productVersion;

        // Buscar setups activos para este producto
        $setups = self::forProduct($productId)
            ->active()
            ->get();

        if ($setups->isEmpty()) {
            return null;
        }

        // 1. Buscar coincidencia exacta
        $exactMatch = $setups->firstWhere('version', $productVersion);
        if ($exactMatch) {
            return $exactMatch;
        }

        // 2. Buscar versiones compatibles (versionSetup <= versionProduct)
        $compatibleVersions = $setups->filter(function ($setup) use ($productVersionInt) {
            $setupVersionInt = (int) $setup->version;
            return $setupVersionInt <= $productVersionInt;
        });

        if ($compatibleVersions->isEmpty()) {
            return null;
        }

        // 3. Seleccionar la mayor versión compatible
        return $compatibleVersions->sortByDesc(function ($setup) {
            return (int) $setup->version;
        })->first();
    }

    /* -----------------------------
       Métodos de instancia
    ------------------------------*/

    /**
     * Genera la siguiente versión disponible para este producto
     *
     * @return string Nueva versión en formato YYYYMMDDXX
     */
    public function generateNextVersion(): string
    {
        $currentVersion = (int) $this->version;
        $productId = $this->product_id;

        // Intentar incrementar el último dígito
        $attempts = 0;
        $maxAttempts = 100; // Límite de seguridad

        do {
            $newVersion = $currentVersion + 1;
            $newVersionStr = str_pad((string) $newVersion, 10, '0', STR_PAD_LEFT);

            // Verificar que no exista ya esta versión para este producto
            $exists = self::where('product_id', $productId)
                ->where('version', $newVersionStr)
                ->exists();

            if (!$exists) {
                return $newVersionStr;
            }

            $currentVersion = $newVersion;
            $attempts++;
        } while ($attempts < $maxAttempts);

        // Si llegamos aquí, algo está mal (demasiadas versiones)
        throw new \RuntimeException('No se pudo generar una nueva versión. Demasiadas versiones existentes.');
    }

    /**
     * Crea una copia del setup con una nueva versión
     *
     * @param string|null $newVersion Versión específica o null para auto-generar
     * @return Setup Nuevo setup duplicado
     */
    public function duplicate(?string $newVersion = null): Setup
    {
        if (!$newVersion) {
            $newVersion = $this->generateNextVersion();
        }

        // Verificar que la nueva versión no exista
        $exists = self::where('product_id', $this->product_id)
            ->where('version', $newVersion)
            ->exists();

        if ($exists) {
            throw new \RuntimeException("La versión {$newVersion} ya existe para este producto.");
        }

        return self::create([
            'product_id' => $this->product_id,
            'version' => $newVersion,
            'yaml' => $this->yaml,
            'status' => 'active',
            'description' => $this->description,
            'created_by' => auth()->id(),
            'change_log' => "Duplicado desde versión {$this->version}",
        ]);
    }

    /**
     * Crea una entrada en el historial antes de actualizar
     *
     * @param string|null $changeLog Comentario del cambio
     * @return SetupHistory
     */
    public function createHistoryEntry(?string $changeLog = null): SetupHistory
    {
        return SetupHistory::create([
            'setup_id' => $this->id,
            'yaml' => $this->yaml,
            'change_log' => $changeLog,
            'changed_by' => auth()->id(),
            'created_at' => now(),
        ]);
    }
}


