<?php
/**
 * CDN URL Replacer
 * Handles all URL replacements for CDN integration
 */

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

class USCDN_Replacer {
    
    private static $instance = null;
    
    public static function get_instance() {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    private function __construct() {
        $this->init_filters();
    }
    
    private function init_filters() {
        // Early URL builder filters
        add_filter('wp_get_attachment_url', [$this, 'replace_url'], 10, 1);
        add_filter('style_loader_src', [$this, 'replace_url'], 10, 1);
        add_filter('script_loader_src', [$this, 'replace_url'], 10, 1);
        add_filter('theme_file_uri', [$this, 'replace_url'], 10, 1);
        add_filter('template_directory_uri', [$this, 'replace_url'], 10, 1);
        add_filter('stylesheet_uri', [$this, 'replace_url'], 10, 1);
        add_filter('stylesheet_directory_uri', [$this, 'replace_url'], 10, 1);
        add_filter('content_url', [$this, 'replace_url'], 10, 1);
        add_filter('includes_url', [$this, 'replace_url'], 10, 1);
        add_filter('plugins_url', [$this, 'replace_url'], 10, 1);
        
        // Image specific filters
        add_filter('upload_dir', [$this, 'modify_upload_dir']);
        add_filter('wp_calculate_image_srcset', [$this, 'replace_srcset']);
        add_filter('wp_get_attachment_image_src', [$this, 'replace_image_src'], 10, 4);
        
        // Loader tag safety nets
        add_filter('style_loader_tag', [$this, 'fix_style_tag'], 10, 4);
        add_filter('script_loader_tag', [$this, 'fix_script_tag'], 10, 3);
        
        // Content filters
        add_filter('the_content', [$this, 'replace_in_html'], 11);
        add_filter('widget_text', [$this, 'replace_in_html'], 11);
        add_filter('pagelayer_content', [$this, 'replace_in_html'], 11);
        
        // Output buffer for final HTML
        add_action('template_redirect', [$this, 'start_output_buffer']);
    }
    
    /**
     * Main URL replacement function - CORE CDN LOGIC
     * Based on custom_cdn_url_replacer from original functions.php
     */
    public function replace_url($url) {
        if (!$url || !is_string($url)) {
            return $url;
        }
        
        $origin = $this->get_origin();
        $bases = USCDN_Settings::get_cdn_bases();
        
        // Normalize protocol-relative & root-relative URLs
        $parsed = @parse_url($url);
        $path = $parsed['path'] ?? '';
        
        if (empty($parsed['host'])) {
            if (str_starts_with($url, '//')) {
                $url = $origin['scheme'] . ltrim($url, '/');
            }
            if (str_starts_with($url, '/')) {
                $url = $origin['full'] . $url;
            }
            $parsed = @parse_url($url);
            $path = $parsed['path'] ?? $path;
        }
        
        $url_host = $parsed['host'] ?? '';
        // Only rewrite if the URL comes from our known site/origin hosts
        if (!$this->is_from_source_host($url_host)) {
            return $url;
        }
        
        // Admin-like contexts: skip LINK rewrites, but DO rewrite static assets
        $is_admin_like = $this->is_admin_context();
        if ($is_admin_like && !$this->is_static_asset_path($path)) {
            return $url;
        }
        
        // 1) Images (anywhere in wp-content) → Image CDN
        if (USCDN_Settings::get('enable_image_cdn') && $this->is_image_file($path)) {
            return $this->replace_base($url, $bases['img']);
        }
        
        // 2) Static assets (fonts/css/js) → Static CDN
        if (USCDN_Settings::get('enable_static_cdn') && $this->is_static_asset_path($path)) {
            return $this->replace_base($url, $bases['static']);
        }
        
        // 3) Link CDN (frontend only — admin-like non-static already returned above)
        if (USCDN_Settings::get('enable_link_cdn')) {
            return $this->replace_base($url, $bases['link']);
        }
        
        return $url;
    }
    
    /**
     * Get origin information
     */
    private function get_origin() {
        $host = $_SERVER['HTTP_HOST'] ?? '';
        $scheme = is_ssl() ? 'https://' : 'http://';
        return [
            'full' => $scheme . $host,
            'host' => $host,
            'scheme' => $scheme
        ];
    }
    
    /**
     * Check if URL is from source host
     */
    private function is_from_source_host($url_host) {
        if (empty($url_host)) {
            return true;
        }
        
        $sources = USCDN_Settings::get_source_hosts();
        foreach ($sources as $h) {
            if (strcasecmp($h, $url_host) === 0) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Check if in admin context
     */
    private function is_admin_context() {
        return (function_exists('is_admin') && is_admin()) 
            || (defined('DOING_AJAX') && DOING_AJAX)
            || (isset($_SERVER['REQUEST_URI']) && str_contains($_SERVER['REQUEST_URI'], 'wp-login.php'));
    }
    
    
    /**
     * Check if path is an image file
     */
    private function is_image_file($path) {
        if (!$path) return false;
        
        $image_extensions = [
            '.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', 
            '.ico', '.bmp', '.tiff', '.tif', '.avif'
        ];
        
        $path_lower = strtolower($path);
        foreach ($image_extensions as $ext) {
            if (str_ends_with($path_lower, $ext)) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * Check if path is a static asset (fonts, CSS, JS - NOT images)
     */
    private function is_static_asset_path($path) {
        if (!$path) return false;
        
        // Check for font, CSS, and JS extensions (NOT image extensions)
        $static_extensions = ['.woff2', '.woff', '.ttf', '.otf', '.eot', '.css', '.js'];
        $path_lower = strtolower($path);
        
        foreach ($static_extensions as $ext) {
            if (str_ends_with($path_lower, $ext)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Replace URL base
     */
    private function replace_base($url, $new_base) {
        if (empty($new_base)) {
            return $url;
        }
        
        $p = @parse_url($url);
        if (!$p || empty($p['path'])) {
            return $url;
        }
        
        $base = rtrim($new_base, '/');
        $path = '/' . ltrim($p['path'], '/');
        $query = isset($p['query']) ? ('?' . $p['query']) : '';
        $fragment = isset($p['fragment']) ? ('#' . $p['fragment']) : '';
        
        return $base . $path . $query . $fragment;
    }
    
    /**
     * Modify upload directory
     */
    public function modify_upload_dir($dirs) {
        if (USCDN_Settings::get('enable_image_cdn')) {
            $bases = USCDN_Settings::get_cdn_bases();
            if (!empty($bases['img'])) {
                $dirs['baseurl'] = $bases['img'] . '/wp-content/uploads';
            }
        }
        return $dirs;
    }
    
    /**
     * Replace srcset URLs
     */
    public function replace_srcset($sources) {
        if (!is_array($sources)) {
            return $sources;
        }
        
        foreach ($sources as &$candidate) {
            if (!empty($candidate['url'])) {
                $candidate['url'] = $this->replace_url($candidate['url']);
            }
        }
        return $sources;
    }
    
    /**
     * Replace image src
     */
    public function replace_image_src($image, $attachment_id, $size, $icon) {
        if (is_array($image) && !empty($image[0])) {
            $image[0] = $this->replace_url($image[0]);
        }
        return $image;
    }
    
    /**
     * Fix style loader tag
     */
    public function fix_style_tag($html, $handle, $href, $media) {
        $fixed = $this->replace_url($href);
        if ($fixed && $fixed !== $href) {
            $html = str_replace($href, esc_url($fixed), $html);
        }
        return $html;
    }
    
    /**
     * Fix script loader tag
     */
    public function fix_script_tag($tag, $handle, $src) {
        $fixed = $this->replace_url($src);
        if ($fixed && $fixed !== $src) {
            $tag = str_replace($src, esc_url($fixed), $tag);
        }
        return $tag;
    }
    
    /**
     * Replace URLs in HTML content
     * Based on custom_cdn_replace_in_html from original functions.php
     */
    public function replace_in_html($html) {
        if (!is_string($html) || $html === '') {
            return $html;
        }
        
        $origin = $this->get_origin();
        $bases = USCDN_Settings::get_cdn_bases();
        $hosts = USCDN_Settings::get_source_hosts();
        $scheme = is_ssl() ? 'https://' : 'http://';
        
        // ---------- 1) Fast absolute replacements (safe, path-scoped) ----------
        $replacements = [];
        $static_dirs = [
            '/wp-content/themes/',
            '/wp-content/plugins/',
            '/wp-includes/',
            '/wp-content/et-cache/'  // ✅ cover Divi unified CSS
        ];
        
        foreach ($hosts as $h) {
            $base = $scheme . $h;
            
            // Link CDN (if enabled - will be overridden by specific replacements below)
            if (USCDN_Settings::get('enable_link_cdn') && $bases['link']) {
                $replacements[$base] = $bases['link'];
            }
        }
        
        // Now do regex-based replacements for proper image/static routing
        if (USCDN_Settings::get('enable_image_cdn') && $bases['img']) {
            $html = $this->replace_images_in_html($html, $bases['img']);
        }
        
        if (USCDN_Settings::get('enable_static_cdn') && $bases['static']) {
            $html = $this->replace_static_in_html($html, $bases['static']);
        }
        
        foreach ($replacements as $from => $to) {
            $html = str_replace($from, $to, $html);
        }
        
        // ---------- 2) Fix protocol-relative href/src="//host/..." ----------
        if ($hosts) {
            $host_alt = implode('|', array_map(fn($h) => preg_quote($h, '/'), $hosts));
            
            // Convert only attribute values; then run through CDN replacer
            $html = preg_replace_callback(
                '/\b((?:href|src)\s*=\s*["\'])\/\/(' . $host_alt . ')\/([^"\']+)(["\'])/i',
                function($m) use ($scheme) {
                    $abs = $scheme . $m[2] . '/' . ltrim($m[3], '/');
                    $cdn = $this->replace_url($abs);
                    return $m[1] . esc_url($cdn) . $m[4];
                },
                $html
            );
        }
        
        // ---------- 3) Repair malformed "https:/..." in href/src (single slash) ----------
        $html = preg_replace_callback(
            '/\b((?:href|src)\s*=\s*["\'])https:\/(?!\/)([^"\']+)(["\'])/i',
            function($m) {
                // Normalize to https:// and route via replacer
                $abs = 'https://' . ltrim($m[2], '/');
                $cdn = $this->replace_url($abs);
                return $m[1] . esc_url($cdn) . $m[3];
            },
            $html
        );
        
        // ---------- 4) Inline CSS url(...) absolute hosts → CDN ----------
        if ($hosts && USCDN_Settings::get('enable_static_cdn') && $bases['static']) {
            $host_alt = implode('|', array_map(fn($h) => preg_quote($h, '/'), $hosts));
            $html = preg_replace_callback(
                '/url\((["\']?)(https?:)?\/\/(' . $host_alt . ')\/([^"\')]+)(["\']?)\)/i',
                function ($m) {
                    $proto = $m[2] ?: (is_ssl() ? 'https:' : 'http:');
                    $abs   = $proto . '//' . $m[3] . '/' . ltrim($m[4], '/');
                    $cdn   = $this->replace_url($abs);
                    return 'url(' . $m[1] . $cdn . $m[5] . ')';
                },
                $html
            );
        }
        
        return $html;
    }
    
    
    /**
     * Replace images in HTML with Image CDN
     */
    private function replace_images_in_html($html, $image_cdn_base) {
        $hosts = USCDN_Settings::get_source_hosts();
        if (empty($hosts)) {
            return $html;
        }
        
        $host_pattern = implode('|', array_map(fn($h) => preg_quote($h, '/'), $hosts));
        
        // Match image extensions
        $image_ext = '\.(jpg|jpeg|png|gif|svg|webp|ico|bmp|tiff|tif|avif)';
        
        // Replace absolute image URLs
        $html = preg_replace_callback(
            '/(https?:)?\/\/(' . $host_pattern . ')(\/[^"\'\s<>]*' . $image_ext . ')/i',
            function($matches) use ($image_cdn_base) {
                $path = $matches[3];
                return $image_cdn_base . $path;
            },
            $html
        );
        
        return $html;
    }
    
    /**
     * Replace static assets (fonts, CSS, JS) in HTML with Static CDN
     */
    private function replace_static_in_html($html, $static_cdn_base) {
        $hosts = USCDN_Settings::get_source_hosts();
        if (empty($hosts)) {
            return $html;
        }
        
        $host_pattern = implode('|', array_map(fn($h) => preg_quote($h, '/'), $hosts));
        
        // Match static asset extensions (fonts, CSS, JS - NOT images)
        $static_ext = '\.(woff|woff2|ttf|otf|eot|css|js)';
        
        // Replace absolute static asset URLs
        $html = preg_replace_callback(
            '/(https?:)?\/\/(' . $host_pattern . ')(\/[^"\'\s<>]*' . $static_ext . ')/i',
            function($matches) use ($static_cdn_base) {
                $path = $matches[3];
                return $static_cdn_base . $path;
            },
            $html
        );
        
        return $html;
    }

    /**
     * Start output buffer
     */
    public function start_output_buffer() {
        if (!is_admin()) {
            ob_start([$this, 'replace_in_html']);
        }
    }
}
