<?php

include_once WPS_IC_DIR . 'addons/cdn/cdn-rewrite.php';
include_once WPS_IC_DIR . 'traits/url_key.php';

class wps_ic_combine_css
{

    public static $excludes;
    public static $rewrite;
    public static $isMobile;
    public static $site_url;
    public $zone_name;
    public $cssPath;
    public $filesize_cap;
    public $combine_external;
    public $hmwpReplace;
    public $patterns;
    public $allExcludes;
    public $combine_inline_scripts;
    public $settings;
    public $firstFoundStyle;
    public $combined_url_base;
    public $combined_dir;
    public $urlKey;
    public $url_key_class;
    public $log_criticalCombine;
    public $logger;
    public $criticalCombine;
    public $hmwp_rewrite;
    public $no_content_excludes;
    public $current_file;
    public $file_count;
    public $current_section;
    public $asset_url;
    public $enabledCDN;

    public function __construct()
    {
        $this->cssPath = '';
        $this->url_key_class = new wps_ic_url_key();
        $this->urlKey = $this->url_key_class->setup();
        $this->combined_dir = WPS_IC_COMBINE . $this->urlKey . '/css/';
        $this->combined_url_base = WPS_IC_COMBINE_URL . $this->urlKey . '/css/';
        $this->enabledCDN = false;

        $this::$isMobile = $this->isMobile();

        $this->firstFoundStyle = false;

        self::$excludes = new wps_ic_excludes();
        self::$rewrite = new wps_cdn_rewrite();
        self::$site_url = site_url();
        $this->settings = get_option(WPS_IC_SETTINGS);
        $this->filesize_cap = '100000000000'; //in bytes
        $this->combine_inline_scripts = true;
        $this->combine_external = false;
        $this->allExcludes = self::$excludes->combineCSSExcludes();

        if (!empty($this->settings['serve']['jpg']) && !empty($this->settings['serve']['png']) && !empty($this->settings['serve']['gif']) && !empty($this->settings['serve']['svg'])) {
            $this->enabledCDN = true;
            $cf = get_option(WPS_IC_CF);
            if (!empty($cf['settings']['cdn']) && $cf['settings']['cdn'] == '0') {
                $this->enabledCDN = false;
            }
        }

        if (!empty($_GET['criticalCombine']) || !empty(wpcGetHeader('criticalCombine'))) {
            $this->settings['inline-css'] = '0';
            $this->criticalCombine = true;
            $this->filesize_cap = '10000000000'; //in bytes
            $this->combine_inline_scripts = true;
            $this->combine_external = true;
            $this->allExcludes = array_merge(['media="print"', 'media=\'print\''], $this->allExcludes);
        }

        $this->patterns = '/(<link[^>]*rel=["\']stylesheet["\'][^>]*>)|((?<!<noscript>)<style\b[^>]*>(.*?)<\/style>)|(<link\b[^>]*?onload=["\']this.rel=["\']stylesheet["\']["\'][^>]*>)/si';

        $cf = get_option(WPS_IC_CF);
        $custom_cname = !empty($cf['cname']) ? $cf['cname'] : get_option('ic_custom_cname');
        if (empty($custom_cname) || !$custom_cname) {
            $this->zone_name = get_option('ic_cdn_zone_name');
        } else {
            $this->zone_name = $custom_cname;
        }

        //Check if Hide my WP is active and get replaces
        $this->hmwpReplace = false;
        if (class_exists('HMWP_Classes_ObjController')) {
            $this->hmwpReplace = true;
            $plugin_path = WP_PLUGIN_DIR . '/hide-my-wp/';
            include_once($plugin_path . 'classes/ObjController.php');
            $hmwp_controller = new HMWP_Classes_ObjController();
            $this->hmwp_rewrite = $hmwp_controller::getClass('HMWP_Models_Rewrite');
        }
    }


    public function isMobile()
    {
        if (!empty($_GET['simulate_mobile'])) {
            return true;
        }

        $userAgent = '';
        if (!empty($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'PreloaderAPI') !== false) {
            $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
        }

        // Desktop Detection
        $desktopKeywords = ['windows nt', 'macintosh', 'linux', 'cros', 'x11'];

        foreach ($desktopKeywords as $keyword) {
            if (strpos($userAgent, $keyword) !== false) {
                return false; // Detected a desktop identifier, so it's not a mobile device
            }
        }

        if (isset($_SERVER['HTTP_USER_AGENT'])) {
            // Define an array of mobile device keywords to check against
            $mobileKeywords = ['android', 'iphone', 'ipad', 'ipod', 'windows phone', 'blackberry', 'bb10', 'webos', 'symbian', 'playbook', 'kindle', 'silk', 'opera mini', 'opera mobi', 'palm'];

            // Check if the user agent contains any of the mobile device keywords
            foreach ($mobileKeywords as $keyword) {
                if (strpos($userAgent, $keyword) !== false) {
                    return true; // Found a match, so it's a mobile device
                }
            }
        }

        return false;
    }


    public function pathWalker($path, $find)
    {
        $paths = explode('/', $path);
        $foldersUp = substr_count($find, '../');

        $array = array_splice($paths, 0, -$foldersUp);
        $array = implode('/', $array);

        return $array;
    }


    public function preloadLCP($html)
    {
        $preloadLCP = [];
        preg_match_all('/<img\s[^>]*\bsrc\s*=\s*["\']([^"\']*)["\'][^>]*>/si', $html, $matches);
        if (!empty($matches[1])) {
            $images = array_slice($matches[1], 0, 3);
            foreach ($images as $k => $src) {
                $preloadLCP[] = $src;
            }
        }

        return $preloadLCP;
    }


    public function preparePreloads($html)
    {
        preg_match_all('/<link\s+[^>]*\bhref=(["\'])(.*?)\1[^>]*>/is', $html, $matches);

        $AlreadyLoadedLocaLFonts = [];
        $wpcPreloads = '';
        $wpcPreloadsGenerator = '';

        if (!empty($matches[2])) {
            foreach ($matches[2] as $k => $href) {

                if (strpos($href, '.css') === false && strpos($href, 'fonts.google') === false) {
                    continue;
                }

                // Href is local
                $cleanHref = explode('?', trim($href));
                $cleanHref = trim($cleanHref[0]);

                if (strpos($cleanHref, self::$site_url) !== false) {
                    $path = str_replace([self::$site_url, $this->zone_name, 'https:///m:0/a:', 'https://' . $this->zone_name . '/m:0/a:','https:///m:1/a:', 'https://' . $this->zone_name . '/m:1/a:'], '', $cleanHref);
                    $path = ltrim($path, '/');
                    $relativePath = ABSPATH . $path;

                    try {
                        $content = file_get_contents($relativePath);
                    } catch (Exception $e) {
                        //suppress the warning
                    }

                    if (!empty($content)) {
                        // Get the filename
                        $cssFilename = basename($href);
                        $cssUrlPath = str_replace($cssFilename, '', $href);

                        // Remove the site URL from the Path to retrieve just the path
                        $cssPath = str_replace([self::$site_url . '/', 'http://' . $_SERVER['HTTP_HOST'] . '/'], '', $cssUrlPath);
                        $cssPath = rtrim($cssPath, '/');
                        $this->cssPath = self::$site_url . '/' . $cssPath;

                        // Find All The Fonts
                        $css = $this->fixUrlPaths($content);
                        #$foundFonts = $this->findFonts($css);
                        if (!empty($foundFonts)) {
                            $AlreadyLoaded = [];
                            foreach ($foundFonts as $i => $font) {
                                if (!in_array($font, $AlreadyLoaded)) {
                                    $AlreadyLoaded[] = $font;
                                    #$wpcPreloads[] = "<link rel='preload' href='" . $font . "' as='font' />";
                                }
                            }
                        }

                        // Find All The Images
                        $foundBackgrounds = $this->findBackgrounds($css);
                        if (!empty($foundBackgrounds)) {
                            $AlreadyLoaded = [];
                            foreach ($foundBackgrounds as $i => $bg) {
                                if (!in_array($bg, $AlreadyLoaded)) {
                                    $AlreadyLoaded[] = $bg;
                                    #$wpcPreloads[] = "<link rel='preload' href='" . $bg . "' as='image' />";
                                }
                            }
                        }

                    }
                } elseif (strpos($href, 'fonts.google')) {
                    #$preload = "<link rel='preload' href='" . $href . "' as='style' />";
                    #$wpcPreloads[] = $preload;
                } elseif (strpos($href, 'fontawesome.com')) {
                    if (!in_array($href, $AlreadyLoadedLocaLFonts)) {
                        $AlreadyLoadedLocaLFonts[] = $href;
                        $preload = "<link rel='preload' href='" . $href . "' as='style' />";
                        $wpcPreloads .= $preload;
                    }
                }
            }
        }
        if ($this->is_home_url()) {
            if (!self::$rewrite->is_mobile()) {
                $wpcPreloadsGenerator = self::$rewrite->preload_custom_assets('string', $html);
            } else {
                $wpcPreloadsGenerator = self::$rewrite->preload_custom_assetsMobile('string', $html);
            }
        }

        return $wpcPreloadsGenerator . $wpcPreloads;
    }


    public function fixUrlPaths($css)
    {
        $css = preg_replace_callback('/url(\(((?:[^()]+|(?1))+)\))/m', [$this, 'fixPathsWalker'], $css);

        // Fix URLs inside @import statements
        $css = preg_replace_callback('/@import\s+["\']([^"\']+)["\'];?/i', [$this, 'fixImportPaths'], $css);

        return $css;
    }

    public function findBackgrounds($css)
    {
        $pattern = '/(?:background(?:-image)?\s*:\s*url\s*\(\s*([\'"]?)(.*?)\1\s*\))/si';

        // Perform the regular expression match
        preg_match_all($pattern, $css, $matches);

        // Extracted URLs will be in $matches[1]
        $fontUrls = $matches[2];

        // Filter the URLs based on file extensions (eot, woff, etc.)
        $filteredUrls = array_filter($fontUrls, function ($url) {
            return preg_match('/\.(svg|jpeg|jpg|gif|png)\b/', $url);
        });

        // Remove quotes from the filtered URLs
        $filteredUrls = array_filter(array_map(function ($url) {
            return trim($url, '"\'');
        }, $filteredUrls));


        if (!empty($filteredUrls)) {
            return $filteredUrls;
        }

        return false;
    }

    public function is_home_url()
    {
        $home_url = rtrim(home_url(), '/');
        $current_url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
        $current_url = rtrim($current_url, '/');
        $current_url = explode('?', $current_url);
        $current_url = $current_url[0];
        $home_url = rtrim($home_url, '/');
        $current_url = rtrim($current_url, '/');

        return $home_url === $current_url;
    }

    public function fixImportPaths($matches)
    {
        if (!empty($matches)) {
            $foundUrls = trim($matches[1]);

            if (strpos($foundUrls, 'data:') !== false) {
                return trim($matches[0]);
            } else {
                $cssPath = $this->cssPath;

                $foundUrls = str_replace(['("', "('", '")', "')"], '', $foundUrls);
                $foundUrls = trim($foundUrls, '()');

                if (strpos($foundUrls, '//') === 0 || strpos($foundUrls, 'http') === 0) {
                    return '@import "' . $foundUrls . '";';
                } else {
                    if (strpos($foundUrls, '../') !== false) {
                        $count = substr_count($foundUrls, '../');
                        $newUrl = $this->moveUpDirectories($this->cssPath, $count);
                        $path = str_replace('../', '', $foundUrls);
                        return '@import "' . $newUrl . $path . '";';
                    } elseif (strpos($foundUrls, './') !== false) {
                        $removeRelative = str_replace('./', '', $foundUrls);
                        return '@import "' . $cssPath . '/' . $removeRelative . '";';
                    } elseif (strpos($foundUrls, '/wp-content') !== false && strpos($foundUrls, '/wp-content') == 0) {
                        return '@import "' . self::$site_url . $foundUrls . '";';
                    } elseif (strpos($foundUrls, '/') === 0) {
                        return '@import "' . $cssPath . $foundUrls . '";';
                    } else {
                        return '@import "' . $cssPath . '/' . $foundUrls . '";';
                    }
                }
            }
        }

        return $matches[0];
    }

    public function moveUpDirectories($url, $upCount = 1)
    {
        // Validate input
        if (!is_string($url) || $upCount < 0) {
            return false;
        }

        // Remove any trailing slashes from the URL
        $url = rtrim($url, '/');

        // Split the URL into parts
        $urlParts = parse_url($url);

        // If the URL doesn't have a path, there's nothing to move up
        if (!isset($urlParts['path'])) {
            return $url;
        }

        // Get the path and split it into segments
        $path = explode('/', trim($urlParts['path'], '/'));

        // Move up the specified number of directories
        $path = array_slice($path, 0, -$upCount);

        // Reconstruct the URL
        $urlParts['path'] = '/' . implode('/', $path);

        // Reassemble the URL
        $resultUrl = $urlParts['scheme'] . '://' . $urlParts['host'] . (isset($urlParts['port']) ? ':' . $urlParts['port'] : '') . $urlParts['path'];

        return $resultUrl . '/';
    }

    public function isHtml($string)
    {
        return preg_match("/<[^<]+>/", $string) === 1;
    }

    public function doInline($html)
    {
        $wpcPreloads = [];
        preg_match_all('/<link\s+[^>]*\bhref=(["\'])(.*?)\1[^>]*>/is', $html, $matches);

        $excludes_class = new wps_ic_excludes();

        if (!empty($matches[2])) {
            foreach ($matches[2] as $k => $href) {

                if ($excludes_class->strInArray($matches[0][$k], $excludes_class->inlineCSSExcludes())) {
                    continue;
                }

                if (strpos($href, '.css') === false && strpos($href, 'fonts.google') === false) {
                    continue;
                }

                // Href is local
                $cleanHref = explode('?', trim($href));
                $cleanHref = trim($cleanHref[0]);

                if (strpos($cleanHref, self::$site_url) !== false) {
                    $path = str_replace(self::$site_url, '', $cleanHref);
                    $path = ltrim($path, '/');
                    $relativePath = ABSPATH . '/' . $path;

                    if (!file_exists($relativePath)) {
                        // do nothing
                        continue;
                    }

                    // get the file content
                    $content = file_get_contents($relativePath);

                    if (!empty($content)) {
                        // Check if it's valid CSS
                        // Get the filename
                        $cssFilename = basename($href);
                        $cssUrlPath = str_replace($cssFilename, '', $href);

                        // Remove the site URL from the Path to retrieve just the path
                        $cssPath = str_replace([self::$site_url . '/', 'http://' . $_SERVER['HTTP_HOST'] . '/'], '', $cssUrlPath);
                        $cssPath = rtrim($cssPath, '/');
                        $this->cssPath = self::$site_url . '/' . $cssPath;

                        $content = $this->fixControlCharacter($content);
                        $content = $this->removeCommentsFromCSS($content);
                        $content = $this->removeCharsetFromCSS($content);
                        $content = $this->fixAnimations($content);
                        $content = $this->fixUrlPaths($content);

                        // Find FontFaces
                        $content = $this->findFontFace($content);

                        // Find All The Fonts
                        $foundFonts = $this->findFonts($content);
                        if (!empty($foundFonts)) {
                            $AlreadyLoaded = [];
                            foreach ($foundFonts as $i => $font) {
                                if (!in_array($font, $AlreadyLoaded)) {
                                    $AlreadyLoaded[] = $font;

                                    #$content = str_replace('src:url("'.$font.'") format("woff2");', '', $content);
                                    #$content = str_replace("src:url('".$font."') format('woff2');", '', $content);

                                    if (strpos($font, 'icon') !== false) continue;
                                    $figurePreloadType = $this->figurePreloadType($font);
                                    $wpcPreloads[] = "<link rel='wpc-lazy-font' href='" . $font . "' as='" . $figurePreloadType['as'] . "' type='" . $figurePreloadType['type'] . "' " . $figurePreloadType['extra'] . ">";
                                }
                            }
                        }

                        // Find All The Images
                        #$foundBackgrounds = $this->findBackgrounds($content);
                        if (!empty($foundBackgrounds)) {
                            $AlreadyLoaded = [];
                            foreach ($foundBackgrounds as $i => $bg) {

                                if (strpos(strtolower($bg), 'array') !== false) {
                                    continue;
                                }

                                if (!in_array($bg, $AlreadyLoaded)) {
                                    $AlreadyLoaded[] = $bg;
                                    $figurePreloadType = $this->figurePreloadType($bg);
                                    $wpcPreloads[] = "<link rel='preload' href='" . $bg . "' as='" . $figurePreloadType['as'] . "' type='" . $figurePreloadType['type'] . "'>";
                                }
                            }
                        }

                        $content = $this->minifyCSS($content);
                        $inlinedStyle = '<style type="text/css" id="doInline-' . mt_rand(999, 9999) . '">' . $content . '</style>';
                        $html = str_replace($matches[0][$k], $inlinedStyle, $html);
                    }
                } elseif (strpos($href, 'fonts.google')) {
                    // <link rel='preload' id='et-gf-open-sans-css' href='https://fonts.googleapis.com/css?family=Open+Sans%3A400%2C700&#038;ver=1.3.12' as='style' media='all' onload="this.onload=null;this.rel='stylesheet'" />
                    $preload = "<link rel='preload' href='" . $href . "' as='style' />";
                    $html = str_replace($matches[0][$k], $preload . $matches[0][$k], $html);
                } elseif (strpos($href, 'fontawesome.com')) {
                    // <link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.13.1/css/all.css" integrity="sha384-B9BoFFAuBaCfqw6lxWBZrhg/z4NkwqdBci+E+Sc2XlK/Rz25RYn8Fetb+Aw5irxa" crossorigin="anonymous">
                    $preload = "<link rel='preload' href='" . $href . "' as='style' />";
                    $html = str_replace($matches[0][$k], $preload . $matches[0][$k], $html);
                }
            }
        }

        $preloadFonts = implode('', $wpcPreloads);

        $html = str_replace('<!--WPC_INSERT_PRELOAD-->', $preloadFonts, $html);

        return $html;
    }

    public function fixControlCharacter($css)
    {
        $css = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $css);
        return $css;
    }

    public function removeCommentsFromCSS($css)
    {
        // Use a regular expression to remove comments (/* ... */)
        $cssWithoutComments = preg_replace('/\/\*[^*]*\*+([^\/][^*]*\*+)*\//', '', $css);
        #$cssWithoutCommentsAndNewLines = preg_replace('/\/\*[^*]*\*+([^\/][^*]*\*+)*\s*\*\//', '', $css);
        return $cssWithoutComments;
    }

    public function removeCharsetFromCSS($css)
    {
        // Use a regular expression to remove @charset declarations
        $cssWithoutCharset = preg_replace('/@charset[^;]+;/', '', $css);
        return $cssWithoutCharset;
    }

    public function fixAnimations($css)
    {
        $replacement = 'will-change: transform, opacity;$0';
        $modifiedCss = preg_replace('/\banimation:\s*[^;]+;/i', $replacement, $css);
        $modifiedCss = preg_replace('/\btransition:\s*[^;]+;/i', $replacement, $modifiedCss);
        return $modifiedCss;
    }

    public function findFontFace($css)
    {
        // Read settings once outside the callback to avoid repeated DB queries
        $settings = get_option(WPS_IC_SETTINGS);
        $textFontDisplay = !empty($settings['font-display']) ? $settings['font-display'] : 'swap';
        $iconFontDisplay = !empty($settings['icon-font-display']) ? $settings['icon-font-display'] : 'block';

        return preg_replace_callback('/@font-face\s*{[^}]+}/sim', function ($fontface) use ($textFontDisplay, $iconFontDisplay) {
            $fontFamily = $fontStyle = $fontWeight = $woffUrl = '';
            $urlFound = false;

            // Try to match .woff or .woff2 URL
            if (preg_match('/url\((["\']?)([^)]+\.(woff2?))\1\)/si', $fontface[0], $matchesWoffUrl)) {
                $woffUrl = $matchesWoffUrl[2];
                $urlFound = true;
            }

            // Extract font-family, font-style, and font-weight
            if (preg_match('/font-family\s*:\s*([^;]+);/si', $fontface[0], $matchesFontFamily)) {
                $fontFamily = "font-family: " . $matchesFontFamily[1] . ";";
            }
            if (preg_match('/font-style\s*:\s*([^;]+);/si', $fontface[0], $matchesStyle)) {
                $fontStyle = 'font-style: ' . $matchesStyle[1] . ';';
            }
            if (preg_match('/font-weight\s*:\s*([^;]+);/si', $fontface[0], $matchesWeight)) {
                $fontWeight = 'font-weight: ' . $matchesWeight[1] . ';';
            }

            if ($urlFound) {
                $format = strpos($woffUrl, '.woff2') !== false ? 'woff2' : 'woff';

                // Detect icon fonts — use block to prevent garbled characters
                $fontDisplayValue = $textFontDisplay;
                $familyRaw = isset($matchesFontFamily[1]) ? strtolower(trim($matchesFontFamily[1])) : '';
                if (preg_match('/icon|awesome|fa[- 0-9]|material|dashicon|glyphicon|icomoon|ionicon|line.?awesome|themify|elegant|feather|simple.?line/i', $familyRaw)) {
                    $fontDisplayValue = $iconFontDisplay;
                }

                return "@font-face{{$fontFamily}{$fontStyle}{$fontWeight}font-display:{$fontDisplayValue};src:url(\"$woffUrl\") format(\"$format\");}";
            } else {
                return $fontface[0];
            }
        }, $css);
    }

    public function findFonts($css)
    {
        // Define the regular expression pattern
        $pattern = '/url\(([^)]+)\)/si';

        // Perform the regular expression match
        preg_match_all($pattern, $css, $matches);

        // Extracted URLs will be in $matches[1]
        $fontUrls = $matches[1];

        // Filter the URLs based on file extensions (eot, woff, etc.)
        $filteredUrls = array_filter($fontUrls, function ($url) {
            return preg_match('/\.(woff2)\b/', $url);
        });

        // Remove quotes from the filtered URLs
        $filteredUrls = array_filter(array_map(function ($url) {
            return trim($url, '"\'');
        }, $filteredUrls));


        if (!empty($filteredUrls)) {
            return $filteredUrls;
        }

        return false;
    }

    public function figurePreloadType($preloadUrl)
    {
        $type = '';
        $extra = '';
        $ext = pathinfo($preloadUrl, PATHINFO_EXTENSION);
        switch ($ext) {
            case 'css':
                $as = 'style';
                $type = 'text/css';
                break;
            case 'js':
                $as = 'script';
                $type = 'text/javascript';
                break;
            case 'woff':
            case 'woff2':
            case 'ttf':
            case 'otf':
                $extra = 'crossorigin';
                $as = 'font';
                if ($ext == 'woff') {
                    $type = 'font/woff';
                } else if ($ext == 'woff2') {
                    $type = 'font/woff2';
                } else {
                    $type = 'font/' . $ext;
                }
                break;
            case 'jpg':
            case 'jpeg':
            case 'png':
            case 'gif':
            case 'webp':
            case 'svg':
                $as = 'image';
                if ($ext == 'jpg' || $ext == 'jpeg') {
                    $type = 'image/jpg';
                } else if ($ext == 'gif') {
                    $type = 'image/gif';
                } else if ($ext == 'png') {
                    $type = 'image/png';
                } else if ($ext == 'webp') {
                    $type = 'image/webp';
                } else if ($ext == 'svg') {
                    $type = 'image/svg+xml';
                } else if ($ext == 'avif') {
                    $type = 'image/avif';
                }
                break;
            default:
                $as = '';
                break;
        }

        return ['as' => $as, 'type' => $type, 'extra' => $extra];
    }

    public function minifyCSS($css)
    {
        // Remove spaces after colons
        $css = str_replace(': ', ':', $css);

        // Remove whitespace
        $css = str_replace(["\r\n", "\r", "\n", "\t", '  ', '    ', '    '], '', $css);

        $css = preg_replace('/\/\*(.*?)\*\//s', '', $css); // Remove comments
        $css = preg_replace('/\s+/', ' ', $css); // Remove multiple whitespaces
        $css = preg_replace('/\s?([,:;{}])\s?/', '$1', $css); // Remove spaces around selectors and declarations
        $css = preg_replace('/;}/', '}', $css); // Remove trailing semicolons before closing brace

        return $css;
    }

    public function lazyFontawesome($html)
    {
        preg_match_all('/<link\b[^>]*>/is', $html, $matches);

        if (!empty($matches[0])) {
            foreach ($matches[0] as $tag) {
                preg_match('/\brel=(["\'])(.*?)\1/i', $tag, $relMatch);
                preg_match('/\bhref=(["\'])(.*?)\1/i', $tag, $hrefMatch);

                $rel  = $relMatch[2] ?? '';
                $href = $hrefMatch[2] ?? '';

                if (
                    strtolower($rel) === 'stylesheet' &&
                    (
                        strpos($href, 'fontawesome.com') !== false ||
                        strpos($href, 'font-awesome') !== false
                    )
                ) {
                    $preload = "<link rel='preload' href='" . $href . "' as='style' media='all' onload=\"this.onload=null;this.rel='stylesheet'\" />";
                    $html = str_replace($tag, $preload, $html);
                }
            }
        }

        return $html;
    }

    public function fixPathsWalker($matches)
    {

        if (!empty($matches)) {
            $foundUrls = trim($matches[1]);

            if (strpos($foundUrls, 'data:') !== false) {
                return trim($matches[0]);
            } else {

                $cssPath = $this->cssPath;

                $foundUrls = str_replace('("', '', $foundUrls);
                $foundUrls = str_replace("('", '', $foundUrls);
                $foundUrls = str_replace('")', '', $foundUrls);
                $foundUrls = str_replace("')", '', $foundUrls);

                // Remove the wrapping brackets
                $foundUrls = rtrim($foundUrls, ')');
                $foundUrls = ltrim($foundUrls, '(');
                $foundUrls = trim($foundUrls);

                // If the found url has // or http/s, just set on CDN?
                if (strpos($foundUrls, '//') === 0 || strpos($foundUrls, 'http') === 0) {
                    // Real URL, leave alone?
                    return 'url("' . $foundUrls . '")';
                } else {

                    // Remove the wrapping brackets
                    $foundUrls = rtrim($foundUrls, ')');
                    $foundUrls = ltrim($foundUrls, '(');

                    // If the found url has at least one ../ then do something with it
                    if (strpos($foundUrls, '../') !== false) {
                        $count = substr_count($foundUrls, '../');

                        #return print_r(array($this->cssPath, $count),true);

                        $newUrl = $this->moveUpDirectories($this->cssPath, $count);
                        $path = str_replace('../', '', $foundUrls);

                        // Once again, check if the file exists in figured out path
                        #if (file_exists($dirName . '/' . $walker)) {
                        return 'url("' . $newUrl . $path . '")';
                        #}
                    } elseif (strpos($foundUrls, './') !== false) {

                        // Same folder
                        $foundUrls = ltrim($foundUrls, '(');
                        $foundUrls = rtrim($foundUrls, ')');

                        // Get just the clean path, without ../
                        $removeRelative = str_replace('./', '', $foundUrls);

                        // Once again, check if the file exists in figured out path
                        return 'url("' . $cssUrlPath . $removeRelative . '")';
                    } elseif (strpos($foundUrls, '/wp-content') !== false && strpos($foundUrls, '/wp-content') == 0) {

                        $foundUrls = str_replace('("', '', $foundUrls);
                        $foundUrls = str_replace("('", '', $foundUrls);
                        $foundUrls = str_replace('")', '', $foundUrls);
                        $foundUrls = str_replace("')", '', $foundUrls);
                        return 'url("' . self::$site_url . $foundUrls . '")';
                    } elseif (strpos($foundUrls, '/') === 0) {
                        // Handle URLs starting with '/'
                        return 'url("' . $cssPath . $foundUrls . '")';
                    } else {
                        // its relative to the css script
                        return 'url("' . $cssPath . '/' . $foundUrls . '")';
                    }
                }
            }
        }

        return $matches[0];
    }

    public function replaceCSS($matches)
    {
        if (!empty($matches)) {
            $foundUrls = trim($matches[1]);

            if (strpos($foundUrls, 'data:') !== false) {
                return 'url("' . $foundUrls . '")';
            } else {
                return '';
            }
        }

        return $matches[0];
    }

    public function maybe_do_combine($html)
    {

        if (!empty(get_option('wps_log_critCombine'))) {
            $this->log_criticalCombine = true;
            $this->logger = new wps_ic_logger('criticalCombine');
        }

        // Disabled for some reason?!
        if (1 == 0 && $this->combine_exists() && (empty($_GET['forceRecombine']) && !$this->criticalCombine)) {
            $this->no_content_excludes = get_option('wps_no_content_excludes_css');
            if ($this->no_content_excludes !== false) {
                $this->allExcludes = array_merge($this->allExcludes, $this->no_content_excludes);
            }

            $html = $this->replace($html);
            return $html;
        }


        $this->no_content_excludes = [];
        $this->current_file = '';
        $this->file_count = 1;

        $this->setup_dirs();

        $this->current_section = 'header';
        $html = preg_replace_callback('/<head(.*?)<\/head>/si', [$this, 'combine'], $html);
        #return 'bla'.$html;

        if (!$this->criticalCombine) {
            //we want 1 file in criticalCombine so we dont do this
            $this->write_file_and_next();
            $this->current_section = 'footer';
            $this->file_count = 1;
        }

        $html = preg_replace_callback('/<\/head>(.*?)<\/body>/si', [$this, 'combine'], $html);

        $this->write_file_and_next();

        update_option('wps_no_content_excludes_css', $this->no_content_excludes);
        $html = $this->insert_combined_scripts($html);

        return $html;
    }

    public function combine_exists()
    {
        $exists = is_dir($this->combined_dir);
        if ($exists) {
            $exists = (new \FilesystemIterator($this->combined_dir))->valid();
        }

        return $exists;
    }

    public function replace($html)
    {

        $html = preg_replace_callback($this->patterns, [$this, 'remove_scripts'], $html);
        $html = $this->insert_combined_scripts($html);

        return $html;
    }

    public function insert_combined_scripts($html)
    {
        $combined_files = new \FilesystemIterator($this->combined_dir);

        if ($this->criticalCombine) {
            foreach ($combined_files as $file) {
                $url = $this->combined_url_base . basename($file);
                if (strpos($url, 'http://') !== false) {
                    //force https
                    $url = str_replace('http://', 'https://', $url);
                }
                $link = '<link rel="stylesheet" id="wpc-critical-combined-css" href="' . $url . '?hash=' . time() . '" type="text/css" media="all">' . PHP_EOL;
            }

            $html = str_replace('<!--WPC_INSERT_COMBINED_CSS-->', $link, $html);
            return $html;
        }

        $header_links = '';
        $footer_links = '';

        foreach ($combined_files as $file) {
            $url = $this->combined_url_base . basename($file);
            $criticalCSS = new wps_criticalCss();

            $styleSheetType = 'wpc-stylesheet';
            if (strpos($url, 'mobile') !== false) {
                $styleSheetType = 'wpc-mobile-stylesheet';
            }


            if (!empty($this->settings['critical']['css']) && $this->settings['critical']['css'] == '1' && $criticalCSS->criticalExists() !== false) {
                /////////////// Critical CSS Option is Enabled

                //        if (strpos($file, 'wps_header') !== false) {
                //          $header_links .= '<link rel="'.$styleSheetType.'" href="' . self::$rewrite->adjust_src_url($url) . '" type="'.$styleSheetType.'" media="all">' . PHP_EOL;
                //        } else {
                //          $footer_links .= '<link rel="'.$styleSheetType.'" href="' . self::$rewrite->adjust_src_url($url) . '" type="'.$styleSheetType.'" media="all">' . PHP_EOL;
                //        }

                if (self::$isMobile) {
                    // Mobile
                    if (strpos($file, 'wps_mobile') !== false) {
                        if (strpos($file, 'wps_mobile_header') !== false) {
                            $header_links .= '<link rel="stylesheet" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        } else {
                            $footer_links .= '<link rel="stylesheet" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        }
                    }
                } else {
                    // Desktop
                    if (strpos($file, 'wps_mobile') === false) {
                        if (strpos($file, 'wps_header') !== false) {
                            $header_links .= '<link rel="stylesheet" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        } else {
                            $footer_links .= '<link rel="stylesheet" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        }
                    }
                }


            } else if (!empty($this->settings['remove-render-blocking']) && $this->settings['remove-render-blocking'] == '1') {
                //////////////// Remove render blocking option is Enabled

                if (strpos($file, 'wps_header') !== false) {
                    $header_links .= '<link rel="preload" as="style"  onload="this.rel=\'stylesheet\'" defer href="' . $url . '" type="text/css" media="all">' . PHP_EOL;
                } else {
                    $footer_links .= '<link rel="preload" as="style"  onload="this.rel=\'stylesheet\'" defer href="' . $url . '" type="text/css" media="all">' . PHP_EOL;
                }

            } else if (!empty($this->settings['inline-css']) && $this->settings['inline-css'] == '1') {
                /////////////// Inline CSS Option is Enabled

                if (strpos($file, 'wps_header') !== false) {
                    $combineContent = file_get_contents($file->getPathname());

                    if (!empty($combineContent)) {
                        $header_links .= '<style type="text/css" id="' . basename($file) . '">';
                        $header_links .= $this->minifyCSS($combineContent);
                        $header_links .= '</style>';
                    }

                } else {
                    $combineContent = file_get_contents($file->getPathname());

                    if (!empty($combineContent)) {
                        $footer_links .= '<style type="text/css" id="' . basename($file) . '">';
                        $footer_links .= $this->minifyCSS($combineContent);
                        $footer_links .= '</style>';
                    }
                }

            } else {

                // Inline is not enabled, critical is not enabled
                if (self::$isMobile) {
                    // Mobile
                    if (strpos($file, 'wps_mobile') !== false) {

                        #$combineContent = file_get_contents($file->getPathname());

                        if (strpos($file, 'wps_mobile_header') !== false) {
                            $header_links .= '<link rel="preload" as="style" onload="this.rel=\'stylesheet\'" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                            //              $header_links .= '<style type="text/css" id="' . basename($file) . '">';
                            //              $header_links .= $this->minifyCss($combineContent);
                            //              $header_links .= '</style>';
                        } else {
                            $footer_links .= '<link rel="preload" as="style" onload="this.rel=\'stylesheet\'" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                            //              $footer_links .= '<style type="text/css" id="' . basename($file) . '">';
                            //              $footer_links .= $this->minifyCss($combineContent);
                            //              $footer_links .= '</style>';
                        }
                    }
                } else {
                    // Desktop
                    if (strpos($file, 'wps_mobile') === false) {
                        if (strpos($file, 'wps_header') !== false) {
                            $header_links .= '<link rel="preload" as="style" onload="this.rel=\'stylesheet\'" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        } else {
                            $footer_links .= '<link rel="preload" as="style" onload="this.rel=\'stylesheet\'" href="' . self::$rewrite->adjust_src_url($url) . '" type="text/css" media="all"/>' . PHP_EOL;
                        }
                    }
                }

            }
        }

        if ($this->hmwpReplace) {
            //apply their replacements to our combined files because they are doing them before our insert
            foreach ($this->hmwp_rewrite->_replace['from'] as $key => $value) {
                $replace = $this->hmwp_rewrite->_replace['to'][$key];
                $header_links = str_replace($value, $replace, $header_links);
                $footer_links = str_replace($value, $replace, $footer_links);
            }
        }

        //header
        if (!empty($_GET['testcombine'])) {
            $html = preg_replace('/<\/head>/', $header_links . '</head>', $html);
        } else {
            if (!empty($header_links)) {
                $html = str_replace('<!--WPC_INSERT_COMBINED_CSS-->', $header_links, $html);
            }
        }

        //footer
        $html = preg_replace('/<\/body>/', $footer_links . '</body>', $html);

        return $html;
    }

    public function setup_dirs()
    {
        mkdir(WPS_IC_COMBINE . $this->urlKey . '/css', 0777, true);
    }

    public function write_file_and_next()
    {

        $prefix = '';
        if (self::$isMobile) {
            $prefix = 'mobile_';
        }

        if ($this->criticalCombine) {
            file_put_contents($this->combined_dir . 'wps_combined.css', $this->current_file);
            return;
        }

        if ($this->current_file != '') {
            file_put_contents($this->combined_dir . 'wps_' . $prefix . $this->current_section . '_' . $this->file_count . '.css', $this->current_file);
        }

        $this->file_count++;
        $this->current_file = '';
    }

    public function minifyCssOld($css)
    {
        if (!empty($this->settings['css_minify']) && $this->settings['css_minify'] == '1') {
            //      // Remove line breaks and multiple spaces
            //      $css = preg_replace('/\s+/', ' ', $css);
            //
            //      // Remove spaces before and after braces
            //      $css = str_replace(array('{ ', ' }'), array('{', '}'), $css);
            //
            //      // Remove spaces before and after colons
            //      $css = str_replace(': ', ':', $css);
            $css = preg_replace('/\/\*(.*?)\*\//s', '', $css); // Remove comments
            $css = preg_replace('/\s+/', ' ', $css); // Remove multiple whitespaces
            $css = preg_replace('/\s?([,:;{}])\s?/', '$1', $css); // Remove spaces around selectors and declarations
            $css = preg_replace('/;}/', '}', $css); // Remove trailing semicolons before closing brace
        } else {
            // Remove line breaks and multiple spaces
            $css = preg_replace('/\s+/', ' ', $css);
        }
        return $css;
    }

    public function script_combine_and_replace($tag)
    {
        if ($this->log_criticalCombine) {
            $this->logger->log('Starting new script.');
        }

        $tag = trim($tag[0]);
        if (empty($tag)) {
            return $tag;
        }
        $src = '';
        $media_query = null;

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'before') {
            return print_r([$tag], true);
        }

        // Check if the CSS is Excluded
        if (self::$excludes->strInArray($tag, $this->allExcludes)) {
            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'outputs') {
                return print_r([$tag, 'excluded'], true);
            }
            if ($this->log_criticalCombine) {
                $this->logger->log('It is excluded.', true);
            }
            return $tag;
        }

        // If it has ie9 tag exclude by default
        if (strpos($tag, 'ie9') !== false) {
            return $tag;
        }

        // Extract media query if present
        if (preg_match('/media=["\']([^"\']+)["\']/', $tag, $media_match)) {
            $media_query = $media_match[1];
            if ($this->log_criticalCombine) {
                $this->logger->log('Media query found: ' . $media_query);
            }
        }

        if (strpos($tag, '<link') !== false) {
            $is_src_set = preg_match('/href=["|\'](.*?)["|\']/', $tag, $src);
        } elseif (strpos($tag, '<style') !== false) {
            $is_src_set = preg_match('/<style\b[^>]*\bhref=["\'](.*?)["\'][^>]*>/i', $tag, $src);
        }

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'preg') {
            return print_r([$tag], true);
        }

        if ($is_src_set == 1) {
            $src = str_replace('href=', '', $src);
            $src = str_replace("'", "", $src);
            $src = str_replace('"', "", $src);
            $src = $src[0];

            if ($this->log_criticalCombine) {
                $this->logger->log('Src: ' . $src);
            }

            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'pre-output') {
                return print_r([$tag, 'file', $this->combine_external, $src], true);
            }

            if (!$this->combine_external && $this->url_key_class->is_external($src)) {
                if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'outputs') {
                    return print_r([$tag, 'external'], true);
                }
                if ($this->log_criticalCombine) {
                    $this->logger->log('Is External.');
                }
                return $tag;
            } else if ($this->combine_external && $this->url_key_class->is_external($src)) {
                $content = $this->getRemoteContent($src);
            } else {
                $content = $this->getLocalContent($src);
            }

            if (!$content) {
                $this->no_content_excludes[] = $src;
                if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'outputs') {
                    return print_r([$tag, 'no content', 'external' => $this->combine_external, 'is_external' => $this->url_key_class->is_external($src)], true);
                }
                return $tag;
            }

            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'getLocalContent') {
                return print_r(['no-content', $content], true);
            }

            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'checkFile1') {
                return print_r(['check-content', $src, 'external', $this->url_key_class->is_external($src), $content], true);
            }

            //replace relative urls
            $this->asset_url = $src;

            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'oldrewrite') {
                $content = preg_replace_callback("/url(\(((?:[^()])+)\))/i", [$this, 'rewrite_relative_url'], $content);
            } else {
                    $re = '~url\(\s*(?:"([^"]*)"|\'([^\']*)\'|([^)\s]+))\s*\)~i';

                    $content = preg_replace_callback($re, function ($m) {
                        $url = '';
                        foreach ([1, 2, 3] as $i) {
                            if (isset($m[$i]) && $m[$i] !== '') {
                                $url = $m[$i];
                                break;
                            }
                        }

                        if ($url === '') return $m[0];

                        $new = $this->rewrite_relative_url($url);
                        if ($new === '' || $new === null) return $m[0];

                        // Unwrap if rewrite_relative_url mistakenly returned url(...)
                        if (is_string($new) && preg_match('~^\s*url\(\s*(?:"([^"]*)"|\'([^\']*)\'|([^)\s]+))\s*\)\s*$~i', $new, $mm)) {
                            foreach ([1, 2, 3] as $i) {
                                if (isset($mm[$i]) && $mm[$i] !== '') {
                                    $new = $mm[$i];
                                    break;
                                }
                            }
                        }

                        if (!empty($m[1])) return 'url("' . $new . '")';
                        if (!empty($m[2])) return "url('" . $new . "')";
                        return 'url(' . $new . ')';
                    }, $content);

            }


            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'checkFile2') {
                return print_r(['check-content', $src, 'external', $this->url_key_class->is_external($src), $content], true);
            }
        } else if ($this->combine_inline_scripts) {
            $src = 'Inline Script';

            if ($this->log_criticalCombine) {
                $this->logger->log('Is inline.');
            }

            $content = $tag;
            $content = preg_replace('/<style(.*?)>/', '', $content, -1, $count);
            $content = preg_replace('/<\/style>/', '', $content);

            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'pre-output') {
                return print_r([$tag, 'inline', $this->combine_inline_scripts, $content], true);
            }

            if (!$count) {
                //no href, and not a <style> tag
                if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'outputs') {
                    return print_r([$tag, 'not a style tag'], true);
                }
                return $tag;
            }
        } else {
            if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'outputs') {
                return print_r([$tag, 'unknown'], true);
            }
            return $tag;
        }

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'checkFile3') {
            return print_r(['check-content', $src, 'external', $this->url_key_class->is_external($src), $content], true);
        }

        if ($this->log_criticalCombine) {
            $this->logger->log('Fetched.');
        }


        //sometimes php injects a zero width space char at the start of a new script, this clears it
        $content = preg_replace('/^[\pZ\pC]+|[\pZ\pC]+$/u', '', $content);
        $content = str_replace(['@font-face{', '@font-face {'], '@font-face{font-display: swap;', $content);

        // Find BG and replace with mobile BG
        #if ($this::$isMobile) {
        #$content = preg_replace_callback("/background-image:\s*url\((.*?)\)/is", array($this, 'changeBgImageToMobile'), $content);
        #}

        if ($this->enabledCDN) {
            $content = preg_replace_callback('/src:\s*url\("([^"]+\.woff2)"\)\s*format\(\s*\'woff2\'\s*\);/is', [$this, 'changeFontToCDN'], $content);
        }

        $this->current_file .= "/* SCRIPT : $src */" . PHP_EOL;
        // Wrap content in media query if it exists
        if ($media_query) {
            $this->current_file .= "@media " . $media_query . " {" . PHP_EOL;
            $this->current_file .= $content . PHP_EOL;
            $this->current_file .= "}" . PHP_EOL;
        } else {
            $this->current_file .= $content . PHP_EOL;
        }

        #if (mb_strlen($this->current_file, '8bit') >= $this->filesize_cap) {
        $this->write_file_and_next();
        #}

        if (!$this->firstFoundStyle) {
            $this->firstFoundStyle = true;
            return '<!--WPC_INSERT_COMBINED_CSS-->';
        } else {
            return '';
        }
    }

    public function getRemoteContent($url)
    {
        if ($this->log_criticalCombine) {
            $this->logger->log('Fetching script content.');
        }

        if (strpos($url, '//') === 0) {
            $url = 'https:' . $url;
        }

        $args = array('user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36', 'headers' => array('Accept' => 'text/css,*/*;q=0.1', 'Accept-Language' => 'en-US,en;q=0.9',));


        $data = wp_remote_get($url, $args);

        //todo Check if file is really css

        if (is_wp_error($data)) {
            if ($this->log_criticalCombine) {
                $this->logger->log('Failed fetching script content: WP_Error.', true);
            }
            return false;
        }

        $response_code = wp_remote_retrieve_response_code($data);

        if ($response_code !== 200) {
            if ($this->log_criticalCombine) {
                $this->logger->log('Failed fetching script content. Response code: ' . $response_code, true);
            }
            return false;
        }

        if ($this->log_criticalCombine) {
            $this->logger->log('Script content fetched.');
        }

        return wp_remote_retrieve_body($data);
    }

    public function getLocalContent($url)
    {
        $output = [];

        if ($this->log_criticalCombine) {
            $this->logger->log('Fetching script content.');
        }

        if ($this->hmwpReplace) {
            //go trougn their replacements and reverse them to get true path to files
            foreach ($this->hmwp_rewrite->_replace['to'] as $key => $value) {
                $replace = $this->hmwp_rewrite->_replace['from'][$key];
                $url = str_replace($value, $replace, $url);
            }
            if ($this->log_criticalCombine) {
                $this->logger->log('Did hidemywp replacements and got ' . $url);
            }
        }

        if (!empty($this->zone_name) && strpos($url, $this->zone_name) !== false) {
            preg_match('/a:(.*?)(\?|$)/', $url, $match);
            $url = trim($match[1]);
        }

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'getLocalContent') {
            $output['abs_path'] = ABSPATH;
            $output['first_url'] = $url;
        }

        // url is: example https://site.com/wp-content/plugins/jeg-elementor-kit/assets/css/elements/main.css

        $url = preg_replace('/\?.*/', '', $url);

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'getLocalContent') {
            $output['preg_replace_url'] = $url;
        }

        $path = wp_make_link_relative($url);
        $path = ltrim($path, '/');

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'getLocalContent') {
            $output['relative'] = $path;
        }

        // Upload Dir Path
        $uploadDir = wp_upload_dir();
        $uploadDir = $uploadDir['basedir'];

        // Includes Path
        $includesPath = ABSPATH . WPINC;

        // Theme Dir Path (Without Active Theme)
        $themePath = get_theme_root();

        // $path relative is example: wp-content/plugins/jeg-elementor-kit/assets/css/elements/main.css
        if (strpos($path, 'wp-content/plugins/') !== false) {
            // Plugins DIR: WP_PLUGIN_DIR
            $pathExploded = explode('wp-content/plugins/', $path);
            $justPath = $pathExploded[1];
            $finalPath = WP_PLUGIN_DIR . '/' . $justPath;
        } else if (strpos($path, 'wp-includes/') !== false) {
            // Uploads DIR: wp_upload_dir()
            $pathExploded = explode('wp-includes/', $path);
            $justPath = $pathExploded[1];
            $finalPath = $includesPath . '/' . $justPath;
        } else if (strpos($path, 'wp-content/uploads/') !== false) {
            // Uploads DIR: wp_upload_dir()
            $pathExploded = explode('wp-content/uploads/', $path);
            $justPath = $pathExploded[1];
            $finalPath = $uploadDir . '/' . $justPath;
        } else if (strpos($path, 'wp-content/themes/') !== false) {
            // Themes Dir: TEMPLATEPATH
            $pathExploded = explode('wp-content/themes/', $path);
            $justPath = $pathExploded[1];
            $finalPath = $themePath . '/' . $justPath;
        } else {
            $finalPath = ABSPATH . $path;
        }

        if (!empty($_GET['dbgCombine']) && $_GET['dbgCombine'] == 'getLocalContent') {
            $output['relative_abs'] = $path;
            $output['file_get_content_path'] = ABSPATH . $path;
            $output['plugin_dir'] = WP_PLUGIN_DIR;
            $output['theme_dir'] = TEMPLATEPATH;
            $output['upload_dir'] = $uploadDir;
            $output['justPath'] = $justPath;
            $output['finalPath'] = $finalPath;
            $output['file_exists'] = file_exists($finalPath);
            $output['content'] = file_get_contents($finalPath);
            #$output['read'] = file_get_contents($justPath);

            /** Workaround if file_get_contents failed */ global $wp_filesystem;

            if (!function_exists('WP_Filesystem')) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
            }

            WP_Filesystem();

            $content = false;
            if ($wp_filesystem && $wp_filesystem->exists($finalPath)) {
                $output['contentWPFS'] = $wp_filesystem->get_contents($finalPath);
            }


            return $output;
        }

        if ($this->log_criticalCombine) {
            $this->logger->log('Fetching script content.' . $finalPath);
        }

        if (file_exists($finalPath)) {
            $content = file_get_contents($finalPath);
        }

        if (!$content) {

            /** Workaround if file_get_contents failed */ global $wp_filesystem;

            if (!function_exists('WP_Filesystem')) {
                require_once ABSPATH . 'wp-admin/includes/file.php';
            }

            WP_Filesystem();

            $content = false;
            if ($wp_filesystem && $wp_filesystem->exists($finalPath)) {
                $content = $wp_filesystem->get_contents($finalPath);
            }

            if (!$content || empty($content)) {

                if ($this->log_criticalCombine) {
                    $this->logger->log('Fetch failed,', true);
                }

                return false;
            }
        }

        if ($this->log_criticalCombine) {
            $this->logger->log('Fetched.');
        }

        return $content;
    }

    public function rewrite_relative_url(string $matched_url): string
    {
        $matched_url = trim($matched_url);
        $matched_url = trim($matched_url, " \t\n\r\0\x0B'\""); // remove quotes if any

        // Skip already-rewritten / external / data URLs
        if ($matched_url === '') return '';
        if (stripos($matched_url, 'data:') === 0) return $matched_url;

        if (strpos($matched_url, $this->zone_name) !== false || strpos($matched_url, 'zapwp.net') !== false) {
            return $matched_url;
        }
        if (strpos($matched_url, 'google') !== false || strpos($matched_url, 'gstatic') !== false || strpos($matched_url, 'typekit') !== false) {
            return $matched_url;
        }

        $asset_url = $this->asset_url;
        $parsed_asset = parse_url($asset_url);
        $home = parse_url(get_home_url());

        $scheme = $parsed_asset['scheme'] ?? ($home['scheme'] ?? 'https');
        $host = $parsed_asset['host'] ?? ($home['host'] ?? '');

        // If it's already absolute (http/https or protocol-relative), just normalize
        if (preg_match('~^https?://~i', $matched_url)) {
            $absolute = $matched_url;
        } elseif (strpos($matched_url, '//') === 0) {
            $absolute = $scheme . ':' . $matched_url;
        } else {
            // Build base directory of the asset (CSS) file
            $asset_path = $parsed_asset['path'] ?? '/';
            $base_dir = rtrim(str_replace(basename($asset_path), '', $asset_path), '/');

            if (strpos($matched_url, '/') === 0) {
                // Root-relative
                $path = $matched_url;
            } else {
                // Relative to CSS directory
                $path = $base_dir . '/' . $matched_url;
            }

            // Normalize /./ and /../ segments
            $path = $this->normalize_path($path);

            $absolute = $scheme . '://' . $host . $path;
        }

        // Apply your "serve" logic BUT return plain URL (no url("..."))
        $lower = strtolower($matched_url);

        $is_font = (strpos($lower, '.eot') !== false || strpos($lower, '.woff') !== false || strpos($lower, '.woff2') !== false || strpos($lower, '.ttf') !== false);
        $is_img = (strpos($lower, '.jpg') !== false || strpos($lower, '.jpeg') !== false || strpos($lower, '.png') !== false || strpos($lower, '.gif') !== false || strpos($lower, '.svg') !== false || strpos($lower, '.webp') !== false);

        if ($is_font && !empty($this->settings['serve']['fonts'])) {
            return 'https://' . $this->zone_name . '/m:0/a:' . $absolute;
        }

        if ($is_img) {
            // respect your per-type flags (minimal example; keep your exact checks if you prefer)
            $serve = false;
            if (strpos($lower, '.jpg') !== false && !empty($this->settings['serve']['jpg'])) $serve = true;
            if (strpos($lower, '.jpeg') !== false && !empty($this->settings['serve']['jpg'])) $serve = true;
            if (strpos($lower, '.png') !== false && !empty($this->settings['serve']['png'])) $serve = true;
            if (strpos($lower, '.gif') !== false && !empty($this->settings['serve']['gif'])) $serve = true;
            if (strpos($lower, '.svg') !== false && !empty($this->settings['serve']['svg'])) $serve = true;

            if ($serve) {
                // you had the same URL for mobile/non-mobile currently
                return 'https://' . $this->zone_name . '/q:u/r:0/wp:0/w:1/u:' . $absolute;
            }
        }

        // default: return absolute URL
        return $absolute;
    }

    /**
     * Normalize a URL path by resolving /./ and /../ segments.
     */
    private function normalize_path(string $path): string
    {
        $is_abs = (strpos($path, '/') === 0);
        $parts = explode('/', $path);
        $out = [];

        foreach ($parts as $p) {
            if ($p === '' || $p === '.') continue;
            if ($p === '..') {
                array_pop($out);
                continue;
            }
            $out[] = $p;
        }

        return ($is_abs ? '/' : '') . implode('/', $out);
    }

    public function changeFontToCDN($html)
    {
        if (!empty($this->settings['font-subsetting']) && $this->settings['font-subsetting'] == '1') {
            if (strpos($html[1], 'icon') === false && strpos($html[1], 'awesome') === false && strpos($html[1], 'lightgallery') === false && strpos($html[1], 'gallery') === false && strpos($html[1], 'side-cart-woocommerce') === false) {
                return 'src:url("https://' . $this->zone_name . '/font:true/a:' . $html[1] . '");';
            }
        }

        return 'src:url("https://' . $this->zone_name . '/m:0/a:' . $html[1] . '");';
    }

    public function changeBgImageToMobile($html)
    {
        if (!$this->isMobile()) {
            return $html[0];
        }

        $bgEntire = $html[0];
        $bgUrl = $html[1];

        $MobileBg = str_replace('m:0/', 'mo:1/', $bgUrl);
        $html = str_replace($bgUrl, $MobileBg, $bgEntire);

        #return print_r(array($bgEntire, $bgUrl, $MobileBg, $html),true);
        return $html;
    }

    public function remove_scripts($tag)
    {
        $tag = $tag[0];
        $src = '';

        if (strpos('rs6', $tag) !== false) {
            return $tag;
        }

        if (!$this->combine_external && $this->url_key_class->is_external($tag)) {
            return $tag;
        }

        if (current_user_can('manage_wpc_settings') || self::$excludes->strInArray($tag, $this->allExcludes)) {
            return $tag;
        }


        $is_src_set = preg_match('/href=["|\'](.*?)["|\']/', $tag, $src);
        if ($is_src_set == 1) {
            //nothing
        } else if ($this->combine_inline_scripts) {
            $src = 'Inline Script';

            $content = $tag;
            $content = preg_replace('/<style(.*?)>/', '', $content, -1, $count);

            if (!$count) {
                //no href, and not a <style> tag
                return $tag;
            }
        } else {
            return $tag;
        }

        if (!$this->firstFoundStyle) {
            $this->firstFoundStyle = true;
            return '<!--WPC_INSERT_COMBINED_CSS-->';
        } else {
            return '';
        }
    }

    public function get_combined_css($html)
    {
        // Reset for processing
        $this->current_file = '';
        $this->combine_external = false;
        $this->combine_inline_scripts = true;

        // Process head section
        if (preg_match('/<head(.*?)<\/head>/si', $html, $head_match)) {
            $this->combine($head_match);
        }

        // Process body section
        if (preg_match('/<\/head>(.*?)<\/body>/si', $html, $body_match)) {
            $this->combine($body_match);
        }

        return $this->current_file;
    }

    public function combine($html)
    {
        $html = $html[0];

        // Run for Cookie Compliant CSS
        if (!empty($_GET['testCompliant'])) {
            $html = $this->cookieCompliantCSS($html);
        }

        // STEP 1: Extract and preserve all <script> tags (including their content)
        $script_placeholders = [];
        $script_counter = 0;

        $html = preg_replace_callback('/<script\b[^>]*>.*?<\/script>/si', function ($match) use (&$script_placeholders, &$script_counter) {
            $placeholder = "___SCRIPT_PLACEHOLDER_{$script_counter}___";
            $script_placeholders[$placeholder] = $match[0];
            $script_counter++;
            return $placeholder;
        }, $html);

        // STEP 2: Now process CSS (scripts are temporarily removed)
        $html = preg_replace_callback($this->patterns, [$this, 'script_combine_and_replace'], $html);

        // STEP 3: Restore all <script> tags
        foreach ($script_placeholders as $placeholder => $original_script) {
            $html = str_replace($placeholder, $original_script, $html);
        }

        return $html;
    }

    public function cookieCompliantCSS($html)
    {
        $pattern = '/<script[^>]*id="cmplz-cookiebanner-js-extra"[^>]*>(.*?)<\/script>/si';
        if (preg_match($pattern, $html, $matches)) {
            $script_content = $matches[1];

            if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '1') {
                return print_r(array($matches), true);
            }

            // 2. Extract the JSON: var complianz = {...};
            if (preg_match('/var complianz\s*=\s*(\{.*?\});/s', $script_content, $json_match)) {
                $json_string = $json_match[1];

                if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '2') {
                    return print_r(array($json_match), true);
                }

                // 3. Decode JSON to PHP array
                $complianz = json_decode($json_string, true);

                if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '3') {
                    return print_r(array($json_string), true);
                }

                if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '4') {
                    return print_r(array($complianz, $complianz['css_file']), true);
                }


                if ($complianz && isset($complianz['css_file'])) {
                    $css_file = $complianz['css_file'];
                    $banner_id = $complianz['user_banner_id'] ?? '1';
                    $type = $complianz['consenttype'] ?? 'optin';

                    // 4. Replace placeholders
                    $css_file_final = str_replace(['{banner_id}', '{type}'], [$banner_id, $type], $css_file);

                    // 5. Insert <link> before </head>
                    #$link_tag = '<link rel="stylesheet" href="' . $css_file_final . '">';

                    if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == 'inject-entities') {
                        $link_tag = htmlentities("<link rel='stylesheet' id='wpc-cmplz-banner' href='" . $css_file_final . "' type='text/css' media='all' />");
                    } else {
                        $link_tag = '<link rel="stylesheet" id="wpc-cmplz-banner" href="' . $css_file_final . '" type="text/css" media="all" />';
                    }

                    if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '5') {
                        return print_r(array($link_tag, $css_file_final), true);
                    }

                    if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '6') {
                        return 'LT:[' . htmlentities($link_tag) . "] CF:[" . htmlentities($css_file_final) . "]";
                    }

                    $pattern = '/<script[^>]*id="cmplz-cookiebanner-js-extra"[^>]*>.*?<\/script>/si';

                    if (preg_match($pattern, $html, $matches)) {
                        $matched_script = $matches[0];

                        // Debug match
                        if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '8') {
                            return print_r(['MATCHED_SCRIPT' => $matched_script, 'LinkRaw' => $link_tag, 'linkEnc' => htmlentities($link_tag)], true);
                        }

                        $html = str_replace($matched_script, $link_tag, $html);
                    } else {
                        return 'REGEX DID NOT MATCH';
                    }

                    return $html;
                }
            }
        }

        if (!empty($_GET['dbgCmplz']) && $_GET['dbgCmplz'] == '1') {
            return print_r(array('not-found', $html), true);
        }

        return $html;
    }
}