<?php

namespace GriffinForms\Admin\Secure;

class FormTheme extends \GriffinForms\Admin\Secure\Format
{
    protected function secureFormStyle($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function securePageStyle($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function secureRowStyle($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function secureFieldStyle($value)
    {
        $styles = $this->secureStyleArray($value);
        if (!is_array($styles)) {
            return array();
        }

        foreach ($styles as $field_type => $field_styles) {
            $flattened = $this->flattenLegacyResponsiveStructure($field_styles);
            $styles[$field_type] = $this->pruneStateOverrides($flattened);
        }

        return $styles;
    }

    protected function secureSubmitButton($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function securePreviousButton($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function secureNextButton($value)
    {
        $styles = $this->flattenLegacyResponsiveStructure(
            $this->secureStyleArray($value)
        );

        return $this->pruneStateOverrides($styles);
    }

    protected function secureButtonTypography($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureFormTitle($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureFormDescription($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function securePageHeading($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function securePageDescription($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureRowHeading($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureRowDescription($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureFieldLabel($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureFieldDescription($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureFieldErrors($value)
    {
        return $this->secureStyleArray($value);
    }

    protected function secureSuccessMessage($value)
    {
        return $this->secureStyleArray($value);
    }

    /**
     * Sanitizes style arrays, allowing only safe CSS property values.
     * Validates colors, dimensions, and other style properties.
     *
     * @param mixed $value The style array to sanitize.
     * @return array Sanitized style array.
     */
    protected function secureStyleArray($value)
    {
        if (!is_array($value) && !is_string($value)) {
            return array();
        }

        // If it's a JSON string, decode it
        if (is_string($value)) {
            $value = json_decode($value, true);
            if (!is_array($value)) {
                return array();
            }
        }

        return $this->recursiveSanitizeStyles($value);
    }

    /**
     * Reduces responsive style arrays down to a single xs breakpoint.
     *
     * @param array $styles
     * @return array
     */
    protected function flattenLegacyResponsiveStructure($styles)
    {
        if (!is_array($styles)) {
            return array();
        }

        // Already flat (state => properties)
        $state_keys = array('default', 'focus', 'hover', 'active');
        foreach ($state_keys as $state_key) {
            if (array_key_exists($state_key, $styles)) {
                return $styles;
            }
        }

        foreach (array('xs', 'sm', 'md', 'lg') as $breakpoint) {
            if (isset($styles[$breakpoint]) && $this->styleBlockHasValues($styles[$breakpoint])) {
                return is_array($styles[$breakpoint]) ? $styles[$breakpoint] : array();
            }
        }

        return $this->styleBlockHasValues($styles) ? $styles : array();
    }

    protected function pruneStateOverrides($styles)
    {
        if (!is_array($styles)) {
            return array();
        }

        if (isset($styles['default']) && is_array($styles['default'])) {
            foreach ($styles as $state => $state_values) {
                if ($state === 'default' || !is_array($state_values)) {
                    continue;
                }

                $styles[$state] = $this->removeMatchingOverrides(
                    $state_values,
                    $styles['default']
                );

                if (!$this->styleBlockHasValues($styles[$state])) {
                    unset($styles[$state]);
                }
            }

            return $styles;
        }

        foreach ($styles as $key => $value) {
            if (is_array($value)) {
                $styles[$key] = $this->pruneStateOverrides($value);
                if (!$this->styleBlockHasValues($styles[$key])) {
                    unset($styles[$key]);
                }
            }
        }

        return $styles;
    }

    protected function removeMatchingOverrides($state_values, $reference_values)
    {
        if (!is_array($state_values)) {
            return $state_values;
        }

        foreach ($state_values as $key => $value) {
            $reference_value = (is_array($reference_values) && array_key_exists($key, $reference_values))
                ? $reference_values[$key]
                : null;

            if (is_array($value)) {
                $state_values[$key] = $this->removeMatchingOverrides(
                    $value,
                    is_array($reference_value) ? $reference_value : array()
                );

                if (!$this->styleBlockHasValues($state_values[$key])) {
                    unset($state_values[$key]);
                }
            } else {
                if ($reference_value !== null && (string) $value === (string) $reference_value) {
                    unset($state_values[$key]);
                }
            }
        }

        return $state_values;
    }

    protected function styleBlockHasValues($value)
    {
        if (is_array($value)) {
            foreach ($value as $item) {
                if ($this->styleBlockHasValues($item)) {
                    return true;
                }
            }

            return false;
        }

        return trim((string) $value) !== '';
    }

    /**
     * Recursively sanitizes nested style arrays.
     *
     * @param array $styles The styles array to sanitize.
     * @return array Sanitized styles.
     */
    protected function recursiveSanitizeStyles($styles)
    {
        $sanitized = array();

        foreach ($styles as $key => $value) {
            // Sanitize the key
            $sanitized_key = sanitize_key($key);

            if (is_array($value)) {
                // Handle legacy dimension structures stored as ['value' => '12', 'unit' => 'px']
                if ($this->isDimensionValueArray($value)) {
                    $combined_value = trim((string) ($value['value'] ?? ''));
                    $unit           = trim((string) ($value['unit'] ?? ''));
                    $full_dimension = $combined_value . $unit;

                    $sanitized[$sanitized_key] = $this->sanitizeStyleValue($sanitized_key, $full_dimension);
                    continue;
                }

                // Recursively sanitize nested arrays
                $sanitized[$sanitized_key] = $this->recursiveSanitizeStyles($value);
            } elseif (is_string($value)) {
                // Sanitize string values (colors, dimensions, etc.)
                $sanitized[$sanitized_key] = $this->sanitizeStyleValue($sanitized_key, $value);
            }
        }

        return $sanitized;
    }

    /**
     * Sanitizes individual style property values based on their type.
     *
     * @param string $property_name The CSS property name.
     * @param string $value The CSS property value.
     * @return string Sanitized value.
     */
    protected function sanitizeStyleValue($property_name, $value)
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        // Handle color properties
        if (strpos($property_name, 'color') !== false) {
            return $this->sanitizeColor($value);
        }

        // Handle dimension properties (px, em, %, etc.)
        if (in_array($property_name, array('font-size', 'letter-spacing', 'border-radius', 'offset_x', 'offset_y', 'blur', 'spread', 'size'))) {
            return $this->sanitizeDimension($value);
        }

        // Handle gap/width properties
        if (in_array($property_name, array('gap', 'width'))) {
            return $this->sanitizeDimension($value);
        }

        // Handle border/text/font styles
        if (in_array($property_name, array('style'))) {
            return $this->sanitizeBorderStyle($value);
        }

        // Handle font weight
        if ($property_name === 'font-weight') {
            return $this->sanitizeFontWeight($value);
        }

        // Handle text transform
        if ($property_name === 'text-transform') {
            return $this->sanitizeTextTransform($value);
        }

        // Handle text alignment
        if ($property_name === 'text-align') {
            return $this->sanitizeTextAlignment($value);
        }

        // Handle font family
        if ($property_name === 'font-family') {
            return $this->sanitizeFontFamily($value);
        }

        if ($property_name === 'custom_css') {
            return $this->sanitizeCustomCss($value);
        }

        // Default: sanitize as text
        return sanitize_text_field($value);
    }

    /**
     * Validates and sanitizes color values (hex, rgb, rgba).
     *
     * @param string $color The color value to sanitize.
     * @return string Sanitized color or default.
     */
    protected function sanitizeColor($color)
    {
        $color = trim($color);
        if ($color === '') {
            return '';
        }

        // Hex color
        if (preg_match('/^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/', $color)) {
            return sanitize_hex_color($color);
        }

        // RGB/RGBA color
        if (preg_match('/^rgba?\s*\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(\s*,\s*[\d.]+)?\s*\)$/i', $color)) {
            return sanitize_text_field($color);
        }

        // Named color
        if (preg_match('/^[a-z]+$/i', $color)) {
            return sanitize_text_field($color);
        }

        return '';
    }

    /**
     * Validates and sanitizes dimension values (px, em, %, rem, etc.).
     *
     * @param string $dimension The dimension value to sanitize.
     * @return string Sanitized dimension or default.
     */
    protected function sanitizeDimension($dimension)
    {
        $dimension = trim($dimension);

        // Allow positive numbers with units: px, em, %, rem, vh, vw, etc.
        if (preg_match('/^-?[\d.]+\s*(px|em|%|rem|vh|vw|pt|cm|mm)?$/', $dimension)) {
            return sanitize_text_field($dimension);
        }

        return '';
    }

    /**
     * Detects legacy dimension arrays that contain value/unit pairs.
     *
     * @param array $value Raw value array.
     * @return bool
     */
    protected function isDimensionValueArray($value)
    {
        if (!is_array($value)) {
            return false;
        }

        $keys = array_keys($value);
        sort($keys);

        $allowed_keys = array('unit', 'value');
        sort($allowed_keys);

        // Allow arrays that only contain value+unit (order agnostic)
        if ($keys === $allowed_keys || $keys === array('value')) {
            return true;
        }

        return false;
    }

    /**
     * Validates border style values.
     *
     * @param string $style The border style to validate.
     * @return string Validated style or default.
     */
    protected function sanitizeBorderStyle($style)
    {
        $valid_styles = array('solid', 'dashed', 'dotted', 'double', 'groove', 'ridge', 'inset', 'outset', 'none');
        $style = strtolower(trim($style));

        if (in_array($style, $valid_styles)) {
            return $style;
        }

        return 'solid'; // Default
    }

    /**
     * Validates font weight values.
     *
     * @param string $weight The font weight to validate.
     * @return string Validated weight or default.
     */
    protected function sanitizeFontWeight($weight)
    {
        $weight = trim($weight);

        if ($weight === '') {
            return '400';
        }

        if (preg_match('/^\d{3}$/', $weight)) {
            $numeric = (int) $weight;
            if ($numeric >= 100 && $numeric <= 900) {
                return (string) $numeric;
            }
        }

        return '400'; // Default
    }

    /**
     * Validates text transform values.
     *
     * @param string $transform The text transform to validate.
     * @return string Validated transform or default.
     */
    protected function sanitizeTextTransform($transform)
    {
        $valid_transforms = array('none', 'uppercase', 'lowercase', 'capitalize');
        $transform = strtolower(trim($transform));

        if (in_array($transform, $valid_transforms)) {
            return $transform;
        }

        return 'none'; // Default
    }

    /**
     * Validates text alignment values.
     *
     * @param string $alignment The text alignment to validate.
     * @return string Validated alignment or default.
     */
    protected function sanitizeTextAlignment($alignment)
    {
        $valid_alignments = array('left', 'center', 'right', 'justify');
        $alignment = strtolower(trim($alignment));

        if (in_array($alignment, $valid_alignments)) {
            return $alignment;
        }

        return 'left'; // Default
    }

    /**
     * Validates font family values.
     *
     * @param string $font_family The font family to validate.
     * @return string Validated font family or default.
     */
    protected function sanitizeFontFamily($font_family)
    {
        $font_family = trim($font_family);

        if ($font_family === '') {
            return 'inherit';
        }

        // Get allowed fonts from FontRegistry (single source of truth)
        $registry = new \GriffinForms\Admin\Html\FormControls\FormTheme\FontRegistry();
        $all_fonts = $registry->getCombined();
        $allowed_fonts = array_map('strtolower', array_keys($all_fonts));

        // Add generic font families
        $allowed_fonts = array_merge($allowed_fonts, array(
            'serif',
            'sans-serif',
            'monospace',
            'system-ui',
            '-apple-system',
            'blinkmacsystemfont'
        ));

        $families = array_map('trim', explode(',', $font_family));
        $sanitized = array();

        foreach ($families as $family) {
            if ($family === '') {
                continue;
            }

            $quoted = ($family[0] === '"' || $family[0] === "'");
            $unquoted = trim($family, "\"'");
            $lower = strtolower($unquoted);

            if (in_array($lower, $allowed_fonts, true)) {
                $clean = $unquoted;
            } else {
                // Allow custom font names by stripping unsafe characters
                $clean = preg_replace('/[^a-zA-Z0-9\-\s]/', '', $unquoted);
            }

            if ($clean === '') {
                continue;
            }

            // Quote font names with spaces (multi-word fonts like "Libre Franklin")
            // CSS spec requires quotes for font names containing spaces
            if (strpos($clean, ' ') !== false) {
                $clean = '"' . $clean . '"';
            } elseif ($quoted) {
                // Preserve original quoting if it was quoted
                $clean = '"' . $clean . '"';
            }

            $sanitized[] = $clean;
        }

        if (empty($sanitized)) {
            return 'inherit';
        }

        return implode(', ', $sanitized);
    }

    /**
     * Sanitizes custom CSS snippets while retaining necessary characters.
     *
     * @param string $css Custom CSS value.
     * @return string Sanitized CSS.
     */
    protected function sanitizeCustomCss($css)
    {
        $css = trim($css);

        if ($css === '') {
            return '';
        }

        // Strip HTML tags but keep CSS punctuation and keywords
        $css = wp_strip_all_tags($css);

        // Allow common CSS characters, remove anything unexpected
        $css = preg_replace('/[^a-zA-Z0-9\s\-\_\.\#\:\;\,\%\(\)\'\"\/\!\{\}@]/', '', $css);

        return $css;
    }
}
