<?php

namespace App\Services\ScssCdn;

use App\Models\ScssCdn\ScssCdnBundle;
use App\Models\ScssCdn\ScssCdnFile;
use Illuminate\Support\Str;

class ScssCdnImportParser
{
    /**
     * Detecta todas las directivas @import en un contenido
     *
     * @param string $content
     * @return array Array de imports encontrados ['original' => string, 'path' => string, 'line' => int]
     */
    public static function parseImports(string $content): array
    {
        $imports = [];
        $lines = explode("\n", $content);

        // Patrones para detectar @import
        $patterns = [
            // @import "file";
            '/@import\s+["\']([^"\']+)["\']\s*;/',
            // @import url('file');
            '/@import\s+url\(["\']?([^"\'()]+)["\']?\)\s*;/',
            // @import 'file'
            '/@import\s+["\']([^"\']+)["\']\s*$/',
        ];

        foreach ($lines as $lineNumber => $line) {
            $line = trim($line);
            
            // Saltar comentarios
            if (Str::startsWith($line, '//') || Str::startsWith($line, '/*')) {
                continue;
            }

            foreach ($patterns as $pattern) {
                if (preg_match($pattern, $line, $matches)) {
                    $imports[] = [
                        'original' => $line,
                        'path' => $matches[1],
                        'line' => $lineNumber + 1,
                        'full_match' => $matches[0],
                    ];
                    break; // Solo un import por línea
                }
            }
        }

        return $imports;
    }

    /**
     * Busca un archivo en el bundle basado en una ruta relativa
     *
     * @param ScssCdnBundle $bundle
     * @param string $importPath Ruta del import (puede ser relativa o absoluta)
     * @param string $currentFileRelativePath Ruta del archivo actual
     * @return ScssCdnFile|null
     */
    public static function findFileInBundle(
        ScssCdnBundle $bundle,
        string $importPath,
        string $currentFileRelativePath
    ): ?ScssCdnFile {
        // Normalizar ruta del import
        $importPath = trim($importPath, '"\'');
        
        // Extraer directorio y nombre del archivo
        $pathParts = explode('/', $importPath);
        $fileName = array_pop($pathParts);
        $directory = implode('/', $pathParts);
        
        // Si la ruta ya tiene extensión .scss, quitarla para procesar
        $fileNameWithoutExt = $fileName;
        if (Str::endsWith($fileNameWithoutExt, '.scss')) {
            $fileNameWithoutExt = substr($fileNameWithoutExt, 0, -5);
        }
        
        // En SCSS, los partials pueden tener guión bajo al inicio
        // Intentar primero sin guión bajo, luego con guión bajo
        $possibleFileNames = [
            $fileNameWithoutExt . '.scss',
            '_' . $fileNameWithoutExt . '.scss',
        ];
        
        // Resolver ruta relativa desde el archivo actual
        $resolvedPath = self::resolveRelativePath($importPath . '.scss', $currentFileRelativePath);
        
        // También intentar desde la raíz del bundle (los imports pueden ser relativos a la raíz)
        $rootPath = $directory ? $directory . '/' . $fileNameWithoutExt . '.scss' : $fileNameWithoutExt . '.scss';
        
        // Lista de rutas a intentar
        $pathsToTry = [];
        
        // 1. Ruta resuelta desde el archivo actual (con y sin guión bajo)
        foreach ($possibleFileNames as $possibleFileName) {
            $dir = dirname($resolvedPath);
            if ($dir === '.' || $dir === '') {
                $pathsToTry[] = $possibleFileName;
            } else {
                $pathsToTry[] = $dir . '/' . $possibleFileName;
            }
        }
        
        // 2. Ruta desde la raíz del bundle (con y sin guión bajo)
        foreach ($possibleFileNames as $possibleFileName) {
            if ($directory) {
                $pathsToTry[] = $directory . '/' . $possibleFileName;
            } else {
                $pathsToTry[] = $possibleFileName;
            }
        }
        
        // 3. Si el archivo actual está en una subcarpeta (ej: scss/), intentar desde esa subcarpeta
        $currentDir = dirname($currentFileRelativePath);
        if ($currentDir !== '.' && $currentDir !== '') {
            foreach ($possibleFileNames as $possibleFileName) {
                if ($directory) {
                    $pathsToTry[] = $currentDir . '/' . $directory . '/' . $possibleFileName;
                } else {
                    $pathsToTry[] = $currentDir . '/' . $possibleFileName;
                }
            }
        }
        
        // Eliminar duplicados y valores vacíos
        $pathsToTry = array_unique(array_filter($pathsToTry));
        
        // Intentar cada ruta
        foreach ($pathsToTry as $path) {
            $file = $bundle->files()
                ->where('relative_path', $path)
                ->first();
            
            if ($file) {
                return $file;
            }
        }
        
        return null;
    }

    /**
     * Resuelve una ruta relativa basada en el archivo actual
     *
     * @param string $importPath Ruta del import
     * @param string $currentFilePath Ruta del archivo actual
     * @return string Ruta resuelta
     */
    private static function resolveRelativePath(string $importPath, string $currentFilePath): string
    {
        // Si es ruta absoluta (empieza con /), quitar el /
        if (Str::startsWith($importPath, '/')) {
            return ltrim($importPath, '/');
        }

        // Obtener directorio del archivo actual
        $currentDir = dirname($currentFilePath);
        if ($currentDir === '.') {
            $currentDir = '';
        }

        // Si el import empieza con ../, subir directorios
        while (Str::startsWith($importPath, '../')) {
            $importPath = substr($importPath, 3);
            if ($currentDir === '') {
                // Ya estamos en la raíz, no podemos subir más
                break;
            }
            $currentDir = dirname($currentDir);
            if ($currentDir === '.') {
                $currentDir = '';
            }
        }

        // Construir ruta final
        if ($currentDir === '') {
            return $importPath;
        }

        return $currentDir . '/' . $importPath;
    }

    /**
     * Sustituye @import por URLs firmadas
     *
     * @param string $content Contenido original
     * @param ScssCdnBundle $bundle Bundle al que pertenece el archivo
     * @param string $currentFileRelativePath Ruta del archivo actual
     * @return array ['content' => string, 'replaced' => int, 'not_found' => array]
     */
    public static function replaceImports(
        string $content,
        ScssCdnBundle $bundle,
        string $currentFileRelativePath
    ): array {
        $imports = self::parseImports($content);
        $replaced = 0;
        $notFound = [];

        foreach ($imports as $import) {
            $file = self::findFileInBundle($bundle, $import['path'], $currentFileRelativePath);

            if ($file) {
                // Generar URL firmada
                $url = ScssCdnUrlService::getFileUrl($file);
                
                // Sustituir en el contenido
                $newImport = "@import url('{$url}');";
                $content = str_replace($import['original'], $newImport, $content);
                $replaced++;
            } else {
                $notFound[] = [
                    'path' => $import['path'],
                    'line' => $import['line'],
                ];
            }
        }

        return [
            'content' => $content,
            'replaced' => $replaced,
            'not_found' => $notFound,
        ];
    }

    /**
     * Procesa imports para uso en API, generando URLs con expiración de 1 hora
     * NO guarda el contenido procesado, solo retorna el contenido procesado en memoria
     *
     * @param string $content Contenido del archivo
     * @param ScssCdnBundle $bundle Bundle al que pertenece el archivo
     * @param string $currentFileRelativePath Ruta del archivo actual
     * @return string Contenido procesado con imports reemplazados por URLs firmadas de 1h
     */
    public static function processImportsForApi(
        string $content,
        ScssCdnBundle $bundle,
        string $currentFileRelativePath
    ): string {
        $imports = self::parseImports($content);
        
        if (empty($imports)) {
            return $content;
        }

        // Procesar cada import usando el full_match para reemplazo directo
        foreach ($imports as $import) {
            $file = self::findFileInBundle($bundle, $import['path'], $currentFileRelativePath);

            if ($file) {
                // Generar URL firmada con expiración de 1 hora para API
                $url = ScssCdnUrlService::getFileUrlForApi($file);
                
                // Reemplazar usando preg_replace con el patrón que capturó el import
                $newImport = "@import url('{$url}');";
                
                // Usar los mismos patrones que en parseImports para hacer el reemplazo
                $patterns = [
                    '/@import\s+["\']([^"\']+)["\']\s*;/',
                    '/@import\s+url\(["\']?([^"\'()]+)["\']?\)\s*;/',
                    '/@import\s+["\']([^"\']+)["\']\s*$/',
                ];
                
                foreach ($patterns as $pattern) {
                    // Verificar si este patrón coincide con el import actual
                    if (preg_match($pattern, $import['full_match'])) {
                        // Reemplazar solo si el path coincide
                        $content = preg_replace_callback($pattern, function($matches) use ($import, $url, $newImport) {
                            // Solo reemplazar si el path coincide con el import que estamos procesando
                            if ($matches[1] === $import['path']) {
                                return $newImport;
                            }
                            return $matches[0]; // Mantener original si no coincide
                        }, $content);
                        break;
                    }
                }
            }
            // Si no se encuentra el archivo, dejamos el import original
        }

        return $content;
    }

    /**
     * Procesa todos los archivos de un bundle
     *
     * @param ScssCdnBundle $bundle
     * @param bool $force Reprocesar incluso si ya fueron procesados
     * @return array ['processed' => int, 'errors' => array]
     */
    public static function processBundle(ScssCdnBundle $bundle, bool $force = false): array
    {
        $processed = 0;
        $errors = [];

        $files = $bundle->files;
        
        if ($files->isEmpty()) {
            return [
                'processed' => 0,
                'errors' => ['El bundle no contiene archivos.'],
            ];
        }

        foreach ($files as $file) {
            // Si ya fue procesado y no forzamos, saltar
            if ($file->imports_processed && !$force) {
                continue;
            }

            try {
                $result = self::replaceImports(
                    $file->content,
                    $bundle,
                    $file->relative_path
                );

                // Actualizar contenido y marcar como procesado
                $file->update([
                    'content' => $result['content'],
                    'imports_processed' => true,
                    'updated_by' => auth()->id(),
                ]);

                // Guardar en storage
                ScssCdnStorageService::saveFile(
                    $bundle->product,
                    $bundle,
                    $file->relative_path,
                    $result['content']
                );

                $processed++;

                // Si hay imports no encontrados, agregar a errores
                if (!empty($result['not_found'])) {
                    foreach ($result['not_found'] as $nf) {
                        $errors[] = "Archivo: {$file->relative_path}, línea {$nf['line']}: No se encontró '{$nf['path']}'";
                    }
                }
            } catch (\Exception $e) {
                $errors[] = "Error procesando {$file->relative_path}: " . $e->getMessage();
            }
        }

        return [
            'processed' => $processed,
            'errors' => $errors,
        ];
    }

    /**
     * Procesa un archivo individual
     *
     * @param ScssCdnFile $file
     * @param bool $force
     * @return array ['success' => bool, 'replaced' => int, 'not_found' => array, 'errors' => array]
     */
    public static function processFile(ScssCdnFile $file, bool $force = false): array
    {
        // Si ya fue procesado y no forzamos, retornar
        if ($file->imports_processed && !$force) {
            return [
                'success' => true,
                'replaced' => 0,
                'not_found' => [],
                'errors' => ['El archivo ya fue procesado. Use force=true para reprocesar.'],
            ];
        }

        try {
            $bundle = $file->bundle;
            if (!$bundle) {
                $file->load('bundle');
                $bundle = $file->bundle;
            }

            $result = self::replaceImports(
                $file->content,
                $bundle,
                $file->relative_path
            );

            // Actualizar contenido y marcar como procesado
            $file->update([
                'content' => $result['content'],
                'imports_processed' => true,
                'updated_by' => auth()->id(),
            ]);

            // Guardar en storage
            ScssCdnStorageService::saveFile(
                $bundle->product,
                $bundle,
                $file->relative_path,
                $result['content']
            );

            return [
                'success' => true,
                'replaced' => $result['replaced'],
                'not_found' => $result['not_found'],
                'errors' => [],
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'replaced' => 0,
                'not_found' => [],
                'errors' => [$e->getMessage()],
            ];
        }
    }
}

