<?php

/**
 * CSS Import Processor Service
 *
 * Handles inlining of @import statements in CSS
 *
 * @package ProRank\SEO\Core\Optimization\CSS
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\Optimization\CSS;

use Exception;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}


/**
 * CssImportProcessorService class
 */


class CssImportProcessorService {
    
    /**
     * Path rewriter service
     *
     * @var CssPathRewriterService
     */
    private CssPathRewriterService $path_rewriter;
    
    /**
     * Processed imports cache to prevent circular references
     *
     * @var array
     */
    private array $processed_imports = [];
    
    /**
     * Maximum depth for import nesting
     *
     * @var int
     */
    private int $max_depth = 10;
    
    /**
     * Constructor
     *
     * @param CssPathRewriterService $path_rewriter Path rewriter service
     */
    public function __construct(CssPathRewriterService $path_rewriter) {
        $this->path_rewriter = $path_rewriter;
    }
    
    /**
     * Process CSS content and inline all @import statements
     *
     * @param string $content CSS content
     * @param string $base_path Base path for relative imports
     * @param int $depth Current nesting depth
     * @return string CSS with inlined imports
     * @throws Exception If processing fails
     */
    public function process(string $content, string $base_path, int $depth = 0): string {
        // Check max depth to prevent infinite recursion
        if ($depth > $this->max_depth) {
            return $content;
        }
        
        // Reset processed imports for new top-level processing
        if ($depth === 0) {
            $this->processed_imports = [];
        }
        
        // Find and process @import statements
        $content = $this->processImportStatements($content, $base_path, $depth);
        
        return $content;
    }
    
    /**
     * Process import statements in CSS
     *
     * @param string $content CSS content
     * @param string $base_path Base path for relative imports
     * @param int $depth Current depth
     * @return string Processed CSS
     */
    private function processImportStatements(string $content, string $base_path, int $depth): string {
        // Pattern for @import url()
        $pattern1 = '/@import\s+url\(\s*["\']?(?<url>[^"\')]+)["\']?\s*\)(?<media>[^;]*);?/i';
        $content = preg_replace_callback($pattern1, function($matches) use ($base_path, $depth) {
            return $this->processImport($matches['url'], trim($matches['media']), $base_path, $depth);
        }, $content);
        
        // Pattern for @import "..."
        $pattern2 = '/@import\s+["\'](?<url>[^"\']+)["\'](?<media>[^;]*);?/i';
        $content = preg_replace_callback($pattern2, function($matches) use ($base_path, $depth) {
            return $this->processImport($matches['url'], trim($matches['media']), $base_path, $depth);
        }, $content);
        
        return $content;
    }
    
    /**
     * Process a single import
     *
     * @param string $url Import URL
     * @param string $media Media query
     * @param string $base_path Base path
     * @param int $depth Current depth
     * @return string Inlined content or original import
     */
    private function processImport(string $url, string $media, string $base_path, int $depth): string {
        // Skip external URLs
        if ($this->isExternalUrl($url)) {
            return $this->buildImportStatement($url, $media);
        }
        
        // Resolve file path
        $file_path = $this->resolveFilePath($url, $base_path);
        
        if (!$file_path || !file_exists($file_path)) {
            // Keep original import if file not found
            return $this->buildImportStatement($url, $media);
        }
        
        // Check if already processed (prevent circular imports)
        $file_hash = md5($file_path);
        if (isset($this->processed_imports[$file_hash])) {
            return '/* Circular import detected: ' . basename($file_path) . ' */';
        }
        
        // Mark as processed
        $this->processed_imports[$file_hash] = true;
        
        // Read file content
        $import_content = file_get_contents($file_path);
        if ($import_content === false) {
            return $this->buildImportStatement($url, $media);
        }
        
        // Add source comment for debugging
        $output = "\n/* ProRank CSS - Inlined: " . basename($file_path) . " */\n";
        
        // Rewrite paths in imported content
        $import_dir = dirname($file_path);
        $import_content = $this->path_rewriter->rewrite($import_content, $import_dir, $base_path);
        
        // Recursively process imports in the imported file
        $import_content = $this->process($import_content, $import_dir, $depth + 1);
        
        // Wrap in media query if specified
        if (!empty($media) && $media !== 'all') {
            $output .= "@media {$media} {\n{$import_content}\n}\n";
        } else {
            $output .= $import_content . "\n";
        }
        
        return $output;
    }
    
    /**
     * Build import statement
     *
     * @param string $url URL
     * @param string $media Media query
     * @return string Import statement
     */
    private function buildImportStatement(string $url, string $media): string {
        $statement = '@import url("' . $url . '")';
        
        if (!empty($media) && $media !== 'all') {
            $statement .= ' ' . $media;
        }
        
        return $statement . ';';
    }
    
    /**
     * Check if URL is external
     *
     * @param string $url URL to check
     * @return bool
     */
    private function isExternalUrl(string $url): bool {
        return preg_match('/^(https?:)?\/\//i', $url) === 1;
    }
    
    /**
     * Resolve file path from URL
     *
     * @param string $url URL or relative path
     * @param string $base_path Base directory path
     * @return string|null Resolved file path or null
     */
    private function resolveFilePath(string $url, string $base_path): ?string {
        // Remove query string
        $url = strtok($url, '?');
        
        // Handle root-relative URLs
        if (strpos($url, '/') === 0) {
            // Try to resolve from WordPress root
            $abspath = ABSPATH;
            $file_path = $abspath . ltrim($url, '/');
            
            if (file_exists($file_path)) {
                return $file_path;
            }
            
            // Try content directory
            $content_dir = WP_CONTENT_DIR;
            $content_url = content_url();
            $content_path = wp_parse_url($content_url, PHP_URL_PATH);
            
            if (strpos($url, $content_path) === 0) {
                $relative = substr($url, strlen($content_path));
                $file_path = $content_dir . $relative;
                
                if (file_exists($file_path)) {
                    return $file_path;
                }
            }
            
            return null;
        }
        
        // Handle relative URLs
        $file_path = $base_path . '/' . $url;
        $file_path = $this->normalizePath($file_path);
        
        if (file_exists($file_path)) {
            return $file_path;
        }
        
        return null;
    }
    
    /**
     * Normalize file path
     *
     * @param string $path Path to normalize
     * @return string Normalized path
     */
    private function normalizePath(string $path): string {
        // Convert backslashes to forward slashes
        $path = str_replace('\\', '/', $path);
        
        // Remove double slashes
        $path = preg_replace('#/+#', '/', $path);
        
        // Resolve .. and .
        $parts = explode('/', $path);
        $normalized = [];
        
        foreach ($parts as $part) {
            if ($part === '..') {
                array_pop($normalized);
            } elseif ($part !== '.' && $part !== '') {
                $normalized[] = $part;
            }
        }
        
        return implode('/', $normalized);
    }
    
    /**
     * Extract import statements from CSS
     *
     * @param string $content CSS content
     * @return array Array of imports with url and media
     */
    public function extractImports(string $content): array {
        $imports = [];
        
        // Extract @import url()
        preg_match_all('/@import\s+url\(\s*["\']?([^"\')]+)["\']?\s*\)([^;]*);?/i', $content, $matches, PREG_SET_ORDER);
        foreach ($matches as $match) {
            $imports[] = [
                'url' => $match[1],
                'media' => trim($match[2]),
                'statement' => $match[0],
            ];
        }
        
        // Extract @import "..."
        preg_match_all('/@import\s+["\']([^"\']+)["\']([^;]*);?/i', $content, $matches, PREG_SET_ORDER);
        foreach ($matches as $match) {
            $imports[] = [
                'url' => $match[1],
                'media' => trim($match[2]),
                'statement' => $match[0],
            ];
        }
        
        return $imports;
    }
}