<?php

namespace AdvancedBlockControls;

// Exit if the file is accessed directly
if ( !defined('ABSPATH') ) {
    exit; 
}

class Utils
{
    private $allSettings;
    private $restApiController;

    public function __construct()
    {
        $this->restApiController = new RestApiController();
        $response = $this->restApiController->get_all_settings();
        // Extract the actual settings data from the WP_REST_Response object
        $this->allSettings = $response->get_data();
    }

    // Now this method just returns the settings property
    public function getSettings() {
        $options = $this->allSettings['options'] ?? [];
        $controls = $this->allSettings['controls'] ?? [];
        $deny_list = $this->allSettings['deny_list'] ?? [];

        $mobileLandscape = $options['breakpoints']['MobileLandscape'] ?? [];
        $tabletLandscape = $options['breakpoints']['TabletLandscape'] ?? [];
        $laptop = $options['breakpoints']['Laptop'] ?? [];
        $wideScreen = $options['breakpoints']['WideScreen'] ?? [];
    
        return [
            // Mandatory Breakpoints 
            'brpMobileVal' => $options['breakpoints']['Mobile']['value'] ?? 0,
            'brpMobileUnit' => $options['breakpoints']['Mobile']['unit'] ?? "px",
            'brpTabletVal' => $options['breakpoints']['Tablet']['value'] ?? 0,
            'brpTabletUnit' => $options['breakpoints']['Tablet']['unit'] ?? "px",
            
            // Optional Breakpoints
            'brpMobileLandscapeVal' => $mobileLandscape['value'] ?? null,
            'brpMobileLandscapeEnabled' => $mobileLandscape['enabled'] ?? false,
            'brpMobileLandscapeUnit' => $mobileLandscape['unit'] ?? 'px',

            'brpTabletLandscapeVal' => $tabletLandscape['value'] ?? null,
            'brpTabletLandscapeEnabled' => $tabletLandscape['enabled'] ?? false,
            'brpTabletLandscapeUnit' => $tabletLandscape['unit'] ?? 'px',

            'brpLaptopVal' => $laptop['value'] ?? null,
            'brpLaptopEnabled' => $laptop['enabled'] ?? false,
            'brpLaptopUnit' => $laptop['unit'] ?? 'px',

            'brpWideScreenVal' => $wideScreen['value'] ?? null,
            'brpWideScreenEnabled' => $wideScreen['enabled'] ?? false,
            'brpWideScreenUnit' => $wideScreen['unit'] ?? 'px',

            // Other settings
            'frontEndSpecifityCore' => $options['front_end_specificity_core'] ?? null,
            'frontEndSpecifityThirdParty' => $options['front_end_specificity_third_party'] ?? null,
            'forceRulesCore' => $options['force_rules_core'] ?? null,
            'forceRulesThirdParty' => $options['force_rules_third_party'] ?? null,
            'blocksData' => $controls,
            'denyList' => $deny_list
        ];
    }

    // Check if a block is a core block
    public function isCoreBlock($blockName) {
        return strpos($blockName, "core/") === 0;
    }    
    
    // Get enabled controls for a block
    public function getEnabledBlockControls($blockName) {
        $settings = $this->getSettings();  
           
        // Extract settings needed
        $denyList = $settings['denyList'];
        $blocksData = $settings['blocksData'];

        // Check if the block is in the deny list
        if (array_key_exists($blockName, $denyList)) {
            return null;
        }
    
        $result = isset($blocksData[$blockName]['controls']) ? $blocksData[$blockName]['controls'] : null;
    
        if (!$result) {
            if ($this->isCoreBlock($blockName)) {
                // Get default core controls if not set
                $result = isset($blocksData['core_default']['controls']) ? $blocksData['core_default']['controls'] : null;
            } else {
                // Return default third party controls if not set
                $result = isset($blocksData['third_party_default']['controls']) ? $blocksData['third_party_default']['controls'] : null;
            }
        }
    
        if ($result) {
            // Filter and map the result to include only controls with 'state' => 'active'
            $activeControls = [];
            foreach ($result as $controlId => $controlDetails) {
                if ($controlDetails['state'] === 'active') {
                    $activeControls[] = $controlDetails;
                }
            }
            $result = $activeControls;
        }
    
        return $result;
    }

    // Determining if we should generate css for a control ( No CSS generation for disabled and deleted ones )
    public function shouldGenerateCssForControl($blockName, $controlId) {

        /* For now it should always generate css for a control whether it's disabled or not,
            we have made change for "trash" and decided to remove them so they don't exist in the database anymore */
        return true; 

        $enabledControls = $this->getEnabledBlockControls($blockName);
    
        // Check if there's any enabled control at all
        if (!$enabledControls) {
            return false;
        }
    
        $clonedId = null;
        // Check if the control has a cloned instance and use the id of the cloned one instead
        foreach ($enabledControls as $control) {
            if (isset($control['default_id']) && $control['default_id'] == $controlId) {
                $clonedId = $control['id'];
                break; // Found the cloned ID, no need to continue
            }
        }
    
        if ($clonedId) {
            $controlId = $clonedId;
        }
    
        // Check if the control exists in the enabledControls, then generate CSS for it
        foreach ($enabledControls as $control) {
            if (isset($control['id']) && $control['id'] == $controlId) {
                return true; // Control is enabled and should generate CSS
            }
        }
    
        return false; // Control not found in enabled controls, no need to generate CSS
    }

    // Extract target from target object 
    public function extractTargetFromObject($targetObject) {
        $processTypeValue = function($type, $value) {
            switch ($type) {
                case "":
                case "block-container":
                    return "";
                case "css-id":
                    return $value ? "#{$value}" : "";
                case "css-class":
                    return $value ? ".{$value}" : "";
                case "html-tag":
                case "custom-css-selector":
                    return $value ? $value : "";
                default:
                    return "";
            }
        };
        
        // decode nested JSON
        $targetObject = $this->decodeNestedJSON($targetObject);

        if (!isset($targetObject['common'])) return "";
        $type = isset($targetObject['common']['type']) ? $targetObject['common']['type'] : null;
        $value = isset($targetObject['common']['value']) ? $targetObject['common']['value'] : null;
        return $processTypeValue($type, $value);
    }

    // decode nested JSON
    public function decodeNestedJSON($value) {
        if (is_string($value)) {
            $json = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $value = $this->decodeNestedJSON($json); // Recurse on the nested object
            }
            // If not a JSON string, keep the value as is
        } elseif (is_array($value)) {
            foreach ($value as $key => $val) {
                $value[$key] = $this->decodeNestedJSON($val); // Recurse on the nested object
            }
        }
        return $value;
    }

    // Get target of a control
    public function getControlTarget($blockName, $controlId) {
        $settings = $this->getSettings();  
        $blocksData = $settings['blocksData'];
    
        // Check if the blockName exists in the data
        if (!empty($blocksData[$blockName])) {
            $block = $blocksData[$blockName];
           
            // Check if there are controls for the block and if the specific control exists
            if (!empty($block['controls']) && is_array($block['controls'])) {
               
                foreach ($block['controls'] as $control) {
                    // If a control with the matching controlId is found
                    if (!empty($control['id']) && $control['id'] === $controlId) {
                        
                        $controlTarget = $this->decodeNestedJSON($control['target']);
                        

                        // If the control has a target, return it
                        if ($controlTarget &&
                        ((!empty($controlTarget['common']['type']) && $controlTarget['common']['type'] !== ""))) {
                            return $this->extractTargetFromObject($control['target']);
                        }
                       break;  // Exit the loop if the control is found (even if it doesn't have a target)
                    }
                }
            }

            $blockTarget = $this->decodeNestedJSON($block['target']);
    
            // If no matching control is found or the control does not have a target, return the block's target or an empty string
            return $this->extractTargetFromObject($blockTarget);
        }
    
        // If the block does not exist, return an empty string
        return "";
    }

    public function getControlTypeFromSlug($controlSlug) {
        // Use preg_replace to remove trailing underscore and digits
        return preg_replace('/_\d+$/', '', $controlSlug);
    }

    public function isMobileFirstControl($controlType) {
        // Hardcoded list of mobile-first control types
        $controlsList = ["device_visibility"];
    
        // Check if the control type is in the list
        return in_array($controlType, $controlsList);
    }

    /**
     * Adds a CSS class to the outermost HTML element in block content
     * 
     * @param string $blockContent The HTML content to modify
     * @param string $styleId The style ID to append to the class name
     * @return string The modified HTML content with the class added
     */
    public function addStyleClassToBlockContent($blockContent, $styleId) {
        if (empty($blockContent) || empty($styleId)) {
            return $blockContent;
        }

        // Append the class to the first element that already has a class attribute
        $modified = preg_replace_callback(
            '/<([a-zA-Z0-9]+)([^>]*?)class=["\']([^"\']*)["\']([^>]*)>/',
            function ($matches) use ($styleId) {
                // Reconstruct the element with the new class appended
                $tagName = $matches[1];
                $beforeClass = $matches[2];
                $existingClass = $matches[3];
                $afterClass = $matches[4];
                $newClass = trim($existingClass . ' abc-style-' . $styleId);

                return "<{$tagName}{$beforeClass}class=\"{$newClass}\"{$afterClass}>";
            },
            $blockContent,
            1 // Only modify the first match (outermost element)
        );

        // Fallback: if no class was found, inject one into the first tag
        if (strpos($modified, 'abc-style-' . $styleId) === false) {
            $modified = preg_replace_callback(
                '/<([a-zA-Z0-9]+)([^>]*)>/',
                function ($matches) use ($styleId) {
                    $tagName = $matches[1];
                    $attributes = trim($matches[2]);

                    // Inject new class attribute
                    $injected = $attributes
                        ? "{$attributes} class=\"abc-style-{$styleId}\""
                        : "class=\"abc-style-{$styleId}\"";

                    return "<{$tagName} {$injected}>";
                },
                $modified,
                1
            );
        }

        return $modified;
    }

    public function init()
    {
    }
}