<?php

namespace GriffinForms\Items;

class FormTheme extends \GriffinForms\Items\Format
{
    public const THEME_SCHEMA_VERSION = '2025.11';

    protected const STYLE_PROPERTY_KEYS = array(
        'form_style',
        'page_style',
        'row_style',
        'field_style',
        'button_typography',
        'submit_button',
        'previous_button',
        'next_button',
        'form_title',
        'form_description',
        'page_heading',
        'page_description',
        'row_heading',
        'row_description',
        'field_label',
        'field_description',
        'field_errors',
        'success_message'
    );

    protected $form_style;
    protected $page_style;
    protected $row_style;
    protected $field_style;
    protected $button_typography;
    protected $submit_button;
    protected $previous_button;
    protected $next_button;
    protected $form_title;
    protected $form_description;
    protected $page_heading;
    protected $page_description;
    protected $row_heading;
    protected $row_description;
    protected $field_label;
    protected $field_description;
    protected $field_errors;
    protected $success_message;
    protected $category;
    protected $origin = 'user';

    protected function setFormStyle()
    {
        $default_style = array(
            'background-color' => '#ffffff',
            'background_mode' => 'single',
            'background-image' => '',
            'background-size' => 'cover',
            'background-position' => 'center',
            'background-repeat' => 'no-repeat',
            'padding' => array('top' => '20px', 'right' => '20px', 'bottom' => '20px', 'left' => '20px'),
            'margin' => array('top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
            'border' => array(
                'color' => '#e0e0e0',
                'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                'style' => 'solid'
            ),
            'border-radius' => '0px',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '0px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0)'
            ),
            'custom_css' => ''
        );

        $this->form_style = array(
            'default' => $default_style
        );
    }

    /**
     * Returns the canonical list of style property keys a theme stores.
     * Kept static so import/export layers can reference the schema without
     * bootstrapping a FormTheme instance on every request.
     *
     * @return array
     */
    public static function getStylePropertyKeys()
    {
        return self::STYLE_PROPERTY_KEYS;
    }

    /**
     * Returns a cached snapshot of default style data for every property.
     *
     * @return array
     */
    public static function getDefaultStyleSnapshot()
    {
        static $snapshot = null;
        if ($snapshot !== null) {
            return $snapshot;
        }

        $instance = new self();
        $snapshot = array();
        foreach (self::STYLE_PROPERTY_KEYS as $prop) {
            $snapshot[$prop] = $instance->getProp($prop) ?? array();
        }

        return $snapshot;
    }

    /**
     * Sanitizes user-provided CSS fragments while allowing legitimate characters.
     *
     * @param string $value
     * @return string
     */
    public static function sanitizeCssString($value)
    {
        if (!is_string($value)) {
            return $value;
        }

        // Remove any HTML tags while preserving CSS punctuation.
        $value = wp_kses($value, array());
        $value = preg_replace('/[\x00-\x1F\x7F]+/', '', $value);
        return trim($value);
    }

    /**
     * Escapes a CSS declaration string before printing.
     *
     * @param string $css
     * @return string
     */
    public static function escapeCssDeclarations($css)
    {
        $css = self::sanitizeCssString((string) $css);
        return $css;
    }

    /**
     * Prepares entire <style> tag content.
     *
     * @param string $css
     * @return string
     */
    public static function prepareStyleTagContent($css)
    {
        return self::sanitizeCssString((string) $css);
    }

    protected function setPageStyle()
    {
        $default_style = array(
            'background-color' => '#ffffff',
            'background_mode' => 'single',
            'background-image' => '',
            'background-size' => 'cover',
            'background-position' => 'center',
            'background-repeat' => 'no-repeat',
            'padding' => array('top' => '20px', 'right' => '20px', 'bottom' => '20px', 'left' => '20px'),
            'margin' => array('top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
            'border' => array(
                'color' => '#e0e0e0',
                'width' => array('top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
                'style' => 'solid'
            ),
            'border-radius' => '0px',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '0px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0)'
            ),
            'custom_css' => ''
        );

        $this->page_style = array(
            'default' => $default_style
        );
    }

    protected function setRowStyle()
    {
        $this->row_style = array(
            'default' => array(
                'background-color' => '#ffffff',
                'background_mode' => 'single',
                'background-image' => '',
                'background-size' => 'cover',
                'background-position' => 'center',
                'background-repeat' => 'no-repeat',
                'padding' => array('top' => '10px', 'right' => '0px', 'bottom' => '10px', 'left' => '0px'),
                'margin' => array('top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
                'gap' => '15px',
                'custom_css' => ''
            )
        );
    }

    protected function setFieldStyle()
    {
        $this->field_style = array(
            'text_input' => $this->setTextInputFieldStyle(),
            'textarea' => $this->setTextAreaFieldStyle(),
            'select_input' => $this->setSelectInputFieldStyle(),
            'checkbox_radio' => $this->setCheckboxRadioFieldStyle()
        );
    }

    protected function setTextInputFieldStyle()
    {
        return array(
            'default' => array(
                'background-color' => '#ffffff',
                'color' => '#000000',
                'placeholder_color' => '#999999',
                'padding' => array('top' => '8px', 'right' => '12px', 'bottom' => '8px', 'left' => '12px'),
                'border' => array(
                    'color' => '#cccccc',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'border-radius' => '4px',
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '0px',
                    'spread' => '0px',
                    'color' => 'rgba(0,0,0,0)'
                ),
                'custom_css' => ''
            ),
            'focus' => array(
                'border' => array(
                    'color' => '#007bff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '5px',
                    'spread' => '0px',
                    'color' => 'rgba(0,123,255,0.25)'
                ),
                'outline' => '0',
                'custom_css' => ''
            ),
            'hover' => array(
                'border' => array(
                    'color' => '#80bdff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'custom_css' => ''
            )
        );
    }

    protected function setTextAreaFieldStyle()
    {
        return array(
            'default' => array(
                'background-color' => '#ffffff',
                'color' => '#000000',
                'placeholder_color' => '#999999',
                'padding' => array('top' => '8px', 'right' => '12px', 'bottom' => '8px', 'left' => '12px'),
                'border' => array(
                    'color' => '#cccccc',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'border-radius' => '4px',
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '0px',
                    'spread' => '0px',
                    'color' => 'rgba(0,0,0,0)'
                ),
                'custom_css' => ''
            ),
            'focus' => array(
                'border' => array(
                    'color' => '#007bff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '5px',
                    'spread' => '0px',
                    'color' => 'rgba(0,123,255,0.25)'
                ),
                'outline' => '0',
                'custom_css' => ''
            ),
            'hover' => array(
                'border' => array(
                    'color' => '#80bdff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'custom_css' => ''
            )
        );
    }

    protected function setSelectInputFieldStyle()
    {
        return array(
            'default' => array(
                'background-color' => '#ffffff',
                'color' => '#000000',
                'padding' => array('top' => '8px', 'right' => '12px', 'bottom' => '8px', 'left' => '12px'),
                'border' => array(
                    'color' => '#cccccc',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'border-radius' => '4px',
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '0px',
                    'spread' => '0px',
                    'color' => 'rgba(0,0,0,0)'
                ),
                'custom_css' => ''
            ),
            'focus' => array(
                'border' => array(
                    'color' => '#007bff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '5px',
                    'spread' => '0px',
                    'color' => 'rgba(0,123,255,0.25)'
                ),
                'outline' => '0',
                'custom_css' => ''
            ),
            'hover' => array(
                'border' => array(
                    'color' => '#80bdff',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'custom_css' => ''
            )
        );
    }

    protected function setCheckboxRadioFieldStyle()
    {
        return array(
            'default' => array(
                'accent_color' => '#007bff',
                'size' => '18px',
                'margin' => array('top' => '4px', 'right' => '8px', 'bottom' => '4px', 'left' => '0px'),
                'custom_css' => ''
            )
        );
    }

    protected function setButtonTypography()
    {
        $this->button_typography = array(
            'default' => array(
                'font-family' => '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
                'font-size' => '14px',
                'font-weight' => '700',
                'color' => '#ffffff',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'center'
            )
        );
    }

    protected function setSubmitButton()
    {
        $default_state = array(
            'background-color' => '#007bff',
            'padding' => array('top' => '10px', 'right' => '24px', 'bottom' => '10px', 'left' => '24px'),
            'border' => array(
                'color' => '#0056b3',
                'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                'style' => 'solid'
            ),
            'border-radius' => '4px',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '0px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0)'
            ),
            'custom_css' => ''
        );

        $hover_state = array(
            'background-color' => '#0056b3',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '2px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.2)'
            ),
            'custom_css' => ''
        );

        $focus_state = array(
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,123,255,0.5)'
            ),
            'custom_css' => ''
        );

        $active_state = array(
            'background-color' => '#004085',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '1px',
                'blur' => '3px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.3)'
            ),
            'custom_css' => ''
        );

        $states = array('default' => $default_state, 'hover' => $hover_state, 'focus' => $focus_state, 'active' => $active_state);

        $this->submit_button = $states;
    }

    protected function setPreviousButton()
    {
        $default_state = array(
            'background-color' => '#6c757d',
            'color' => '',
            'padding' => array('top' => '10px', 'right' => '24px', 'bottom' => '10px', 'left' => '24px'),
            'border' => array(
                'color' => '#5a6268',
                'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                'style' => 'solid'
            ),
            'border-radius' => '4px',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '0px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0)'
            ),
            'custom_css' => ''
        );

        $hover_state = array(
            'background-color' => '#5a6268',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '2px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.2)'
            ),
            'custom_css' => ''
        );

        $focus_state = array(
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,123,255,0.5)'
            ),
            'custom_css' => ''
        );

        $active_state = array(
            'background-color' => '#4a5157',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '1px',
                'blur' => '3px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.3)'
            ),
            'custom_css' => ''
        );

        $states = array('default' => $default_state, 'hover' => $hover_state, 'focus' => $focus_state, 'active' => $active_state);

        $this->previous_button = $states;
    }

    protected function setNextButton()
    {
        $default_state = array(
            'background-color' => '#28a745',
            'color' => '',
            'padding' => array('top' => '10px', 'right' => '24px', 'bottom' => '10px', 'left' => '24px'),
            'border' => array(
                'color' => '#218838',
                'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                'style' => 'solid'
            ),
            'border-radius' => '4px',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '0px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0)'
            ),
            'custom_css' => ''
        );

        $hover_state = array(
            'background-color' => '#218838',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '2px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.2)'
            ),
            'custom_css' => ''
        );

        $focus_state = array(
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '0px',
                'blur' => '5px',
                'spread' => '0px',
                'color' => 'rgba(0,123,255,0.5)'
            ),
            'custom_css' => ''
        );

        $active_state = array(
            'background-color' => '#1e7e34',
            'box-shadow' => array(
                'offset_x' => '0px',
                'offset_y' => '1px',
                'blur' => '3px',
                'spread' => '0px',
                'color' => 'rgba(0,0,0,0.3)'
            ),
            'custom_css' => ''
        );

        $states = array('default' => $default_state, 'hover' => $hover_state, 'focus' => $focus_state, 'active' => $active_state);

        $this->next_button = $states;
    }

    protected function setFormTitle()
    {
        $this->form_title = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '24px',
                'color' => '#000000',
                'font-weight' => '700',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setFormDescription()
    {
        $this->form_description = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '14px',
                'color' => '#666666',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setPageHeading()
    {
        $this->page_heading = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '20px',
                'color' => '#000000',
                'font-weight' => '700',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setPageDescription()
    {
        $this->page_description = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '14px',
                'color' => '#666666',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setRowHeading()
    {
        $this->row_heading = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '16px',
                'color' => '#000000',
                'font-weight' => '700',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setRowDescription()
    {
        $this->row_description = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '13px',
                'color' => '#666666',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setFieldLabel()
    {
        $this->field_label = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '14px',
                'color' => '#000000',
                'font-weight' => '700',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setFieldDescription()
    {
        $this->field_description = array(
            'default' => array(
                'font-family' => 'inherit',
                'font-size' => '12px',
                'color' => '#666666',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left'
            )
        );
    }

    protected function setFieldErrors()
    {
        $this->field_errors = array(
            'default' => array(
                // Typography properties
                'font-family' => 'inherit',
                'font-size' => '12px',
                'color' => '#dc3545',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left',
                // Container properties
                'background-color' => 'transparent',
                'padding' => array('top' => '4px', 'right' => '0px', 'bottom' => '4px', 'left' => '0px'),
                'margin' => array('top' => '4px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
                'border' => array(
                    'color' => 'transparent',
                    'width' => array('top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'),
                    'style' => 'solid'
                ),
                'border-radius' => '0px',
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '0px',
                    'spread' => '0px',
                    'color' => 'rgba(0,0,0,0)'
                )
            )
        );
    }

    protected function setSuccessMessage()
    {
        $this->success_message = array(
            'default' => array(
                // Typography properties
                'font-family' => 'inherit',
                'font-size' => '14px',
                'color' => '#155724',
                'font-weight' => '400',
                'letter-spacing' => '0px',
                'text-transform' => 'none',
                'text-align' => 'left',
                // Container properties
                'background-color' => '#d4edda',
                'padding' => array('top' => '12px', 'right' => '12px', 'bottom' => '12px', 'left' => '12px'),
                'margin' => array('top' => '0px', 'right' => '0px', 'bottom' => '16px', 'left' => '0px'),
                'border' => array(
                    'color' => '#c3e6cb',
                    'width' => array('top' => '1px', 'right' => '1px', 'bottom' => '1px', 'left' => '1px'),
                    'style' => 'solid'
                ),
                'border-radius' => '4px',
                'box-shadow' => array(
                    'offset_x' => '0px',
                    'offset_y' => '0px',
                    'blur' => '0px',
                    'spread' => '0px',
                    'color' => 'rgba(0,0,0,0)'
                )
            )
        );
    }

    protected function setCategory()
    {
        $this->category = 'default';
    }

    /**
     * Returns inline CSS for the form wrapper.
     */
    public function getFormStyleCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->form_style ?? array(), $state);
    }

    /**
     * Returns inline CSS for a form page container.
     */
    public function getPageStyleCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->page_style ?? array(), $state);
    }

    /**
     * Returns inline CSS for a row container.
     */
    public function getRowStyleCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->row_style ?? array(), $state);
    }

    /**
     * Returns inline CSS for a field wrapper or control.
     *
     * @param string $type  Field type key (text_input, textarea, select_input, checkbox_radio).
     * @param string $state Style state (default, focus, hover).
     */
    public function getFieldStyleCss($type = 'text_input', $state = 'default')
    {
        $styles = $this->normalizeStyleStructure($this->field_style ?? array());
        if (!isset($styles[$type])) {
            return '';
        }

        $css = $this->styleArrayToCss($this->resolveStyleArray($styles[$type], $state), true);

        if ('select_input' === $type && 'default' === $state) {
            $css .= ' ' . $this->buildSelectArrowCss($this->getSelectCaretColor());
        }

        return $css;
    }

    /**
     * Returns select caret color derived from select_input text color, fallback to #5a5a5a.
     */
    public function getSelectCaretColor()
    {
        $styles = $this->normalizeStyleStructure($this->field_style ?? array());
        if (!isset($styles['select_input'])) {
            return '#5a5a5a';
        }

        $resolved = $this->resolveStyleArray($styles['select_input'], 'default');

        if (!empty($resolved['color'])) {
            return $resolved['color'];
        }

        if (!empty($resolved['border']['color'])) {
            return $resolved['border']['color'];
        }

        return '#5a5a5a';
    }

    protected function buildSelectArrowCss($color)
    {
        if (empty($color)) {
            $color = '#5a5a5a';
        }

        $encoded_color = str_replace('#', '%23', trim($color));
        $arrow_svg = sprintf(
            "data:image/svg+xml,%%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%s' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%%3E%%3Cpolyline points='6 9 12 15 18 9' /%%3E%%3C/svg%%3E",
            $encoded_color
        );

        return sprintf(
            'background-image: url("%s"); background-repeat: no-repeat; background-position: right 18px center; background-size: 14px; padding-right: 44px; appearance: none; -webkit-appearance: none; -moz-appearance: none;',
            $arrow_svg
        );
    }

    public function getSlug()
    {
        $name = $this->getProp('name');
        if (empty($name)) {
            return '';
        }

        return sanitize_title($name);
    }

    /**
     * Returns the accent color for checkbox and radio inputs.
     *
     * @param string $state Style state (default, focus, hover).
     * @return string The accent color hex value or empty string if not set.
     */
    public function getCheckboxRadioAccentColor($state = 'default')
    {
        $styles = $this->normalizeStyleStructure($this->field_style ?? array());
        if (!isset($styles['checkbox_radio'])) {
            return '';
        }

        $resolved = $this->resolveStyleArray($styles['checkbox_radio'], $state);
        return isset($resolved['accent_color']) ? $resolved['accent_color'] : '';
    }

    /**
     * Returns inline CSS for shared button typography.
     */
    public function getButtonTypographyCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->button_typography ?? array(), $state);
    }

    /**
     * Returns inline CSS for the submit button.
     */
    public function getSubmitButtonCss($state = 'default')
    {
        $buttonCss = $this->buildCssFromProperty($this->submit_button ?? array(), $state, true);
        $typographyCss = $this->buildCssFromProperty($this->button_typography ?? array(), 'default', true);

        return trim($typographyCss . ' ' . $buttonCss);
    }

    /**
     * Returns inline CSS for the previous button.
     */
    public function getPreviousButtonCss($state = 'default')
    {
        $buttonCss = $this->buildCssFromProperty($this->previous_button ?? array(), $state, true);
        $typographyCss = $this->buildCssFromProperty($this->button_typography ?? array(), 'default', true);

        return trim($typographyCss . ' ' . $buttonCss);
    }

    /**
     * Returns inline CSS for the next button.
     */
    public function getNextButtonCss($state = 'default')
    {
        $buttonCss = $this->buildCssFromProperty($this->next_button ?? array(), $state, true);
        $typographyCss = $this->buildCssFromProperty($this->button_typography ?? array(), 'default', true);

        return trim($typographyCss . ' ' . $buttonCss);
    }

    /**
     * Returns inline CSS for the form title typography block.
     */
    public function getFormTitleCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->form_title ?? array(), $state);
    }

    /**
     * Returns inline CSS for the form description typography block.
     */
    public function getFormDescriptionCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->form_description ?? array(), $state);
    }

    /**
     * Returns inline CSS for page heading typography.
     */
    public function getPageHeadingCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->page_heading ?? array(), $state);
    }

    /**
     * Returns inline CSS for page description typography.
     */
    public function getPageDescriptionCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->page_description ?? array(), $state);
    }

    /**
     * Returns inline CSS for row heading typography.
     */
    public function getRowHeadingCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->row_heading ?? array(), $state);
    }

    /**
     * Returns inline CSS for row description typography.
     */
    public function getRowDescriptionCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->row_description ?? array(), $state);
    }

    /**
     * Returns inline CSS for field labels.
     */
    public function getFieldLabelCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->field_label ?? array(), $state);
    }

    /**
     * Returns inline CSS for field description text.
     */
    public function getFieldDescriptionCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->field_description ?? array(), $state);
    }

    /**
     * Returns inline CSS for field error messages.
     */
    public function getFieldErrorsCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->field_errors ?? array(), $state);
    }

    /**
     * Returns inline CSS for the success message container.
     */
    public function getSuccessMessageCss($state = 'default')
    {
        return $this->buildCssFromProperty($this->success_message ?? array(), $state);
    }

    /**
     * Gets the label color used in field labels.
     *
     * Returns the color property value from field_label typography in default state.
     * Returns as CSS property value without key name (e.g., "#000000" or "rgb(0,0,0)").
     *
     * @return string Color value from field label, empty string if not found
     */
    public function getLabelColor()
    {
        if (!isset($this->field_label['default']['color'])) {
            return '';
        }

        return $this->field_label['default']['color'];
    }

    /**
     * Gets the error text color.
     *
     * Returns the color property from field_errors typography in default state.
     * Returns as CSS property value without key name (e.g., "#dc3545" or "rgb(220,53,69)").
     *
     * @return string Color value from field errors, empty string if not found
     */
    public function getErrorColor()
    {
        if (!isset($this->field_errors['default']['color'])) {
            return '';
        }

        return $this->field_errors['default']['color'];
    }

    /**
     * Gets the field description text color.
     *
     * Returns the color property from field_description typography in default state.
     * Returns as CSS property value without key name (e.g., "#666666" or "rgb(102,102,102)").
     *
     * @return string Color value from field description, empty string if not found
     */
    public function getFieldDescriptionColor()
    {
        if (!isset($this->field_description['default']['color'])) {
            return '';
        }

        return $this->field_description['default']['color'];
    }

    /**
     * Returns resolved page style array for direct property access.
     */
    public function getPageStyleResolved($state = 'default')
    {
        return $this->resolveStyleArray($this->page_style ?? array(), $state);
    }

    /**
     * Returns resolved field style array for a field type.
     */
    public function getFieldStyleResolved($type = 'text_input', $state = 'default')
    {
        $styles = $this->normalizeStyleStructure($this->field_style ?? array());
        if (!isset($styles[$type])) {
            return array();
        }

        return $this->resolveStyleArray($styles[$type], $state);
    }

    /**
     * Attempts to fetch the primary page text color used by descriptions/headings.
     */
    public function getPageTextColor()
    {
        if (!empty($this->page_description['default']['color'])) {
            return $this->page_description['default']['color'];
        }

        if (!empty($this->page_heading['default']['color'])) {
            return $this->page_heading['default']['color'];
        }

        if (!empty($this->field_label['default']['color'])) {
            return $this->field_label['default']['color'];
        }

        return '';
    }

    /**
     * Gets the page background color.
     *
     * Returns the background-color property from page_style in default state.
     * Returns as CSS property value without key name (e.g., "#ffffff" or "rgb(255,255,255)").
     *
     * @return string Background color value from page style, empty string if not found
     */
    public function getPageBackgroundColor()
    {
        $style = $this->resolveStyleArray($this->page_style ?? array(), 'default');
        return isset($style['background-color']) ? $style['background-color'] : '';
    }

    /**
     * Gets the submit button background color.
     *
     * Returns the background-color property from submit_button in default state.
     * Returns as CSS property value without key name (e.g., "#007bff" or "rgb(0,123,255)").
     *
     * @return string Background color value from submit button, empty string if not found
     */
    public function getSubmitButtonBackgroundColor()
    {
        $style = $this->resolveStyleArray($this->submit_button ?? array(), 'default');
        return isset($style['background-color']) ? $style['background-color'] : '';
    }

    /**
     * Extracts external font families used in the theme.
     * Scans all typography properties and returns unique external font families
     * suitable for enqueueing Google Fonts on the frontend.
     *
     * Uses FontRegistry as the single source of truth for external fonts.
     *
     * @return array Array of unique external font family names
     */
    public function getExternalFontFamilies()
    {
        // Get external fonts from FontRegistry (single source of truth)
        $font_registry = new \GriffinForms\Admin\Html\FormControls\FormTheme\FontRegistry();
        $external_font_names = $font_registry->getExternalFontNames();

        // Properties that contain typography data with font-family
        $typography_properties = array(
            'form_title',
            'form_description',
            'page_heading',
            'page_description',
            'row_heading',
            'row_description',
            'field_label',
            'field_description',
            'field_errors',
            'success_message',
            'button_typography'
        );

        $found_fonts = array();

        // Scan each typography property
        foreach ($typography_properties as $prop) {
            if (!isset($this->$prop) || !is_array($this->$prop)) {
                continue;
            }

            // Extract font-family from default state
            $font_family = $this->extractFontFamilyFromProperty($this->$prop);

            if ($font_family && $font_registry->isExternal($font_family)) {
                $found_fonts[$font_family] = true;
            }
        }

        // Return unique font families as array keys for easy deduplication
        return array_keys($found_fonts);
    }

    /**
     * Helper function to extract font-family from a typography property array.
     *
     * @param array $property Typography property array
     * @return string|null Font family name or null if not found
     */
    protected function extractFontFamilyFromProperty($property)
    {
        if (!is_array($property)) {
            return null;
        }

        // Typography property structure: ['default' => ['font-family' => '...'], ...]
        if (isset($property['default']['font-family'])) {
            return trim((string) $property['default']['font-family']);
        }

        // Fallback: check if property is directly the font-family string
        if (isset($property['font-family'])) {
            return trim((string) $property['font-family']);
        }

        return null;
    }

    /**
     * Build an inline CSS string for a property that stores style definitions.
     *
     * @param mixed       $value      Raw property value (array or serialized/json string).
     * @param string      $state      Requested state (default, hover, focus, etc.).
     * @param string|null $breakpoint Responsive breakpoint (xs, sm, md, lg) when applicable.
     */
    protected function buildCssFromProperty($value, $state = 'default', $use_important = false)
    {
        $style = $this->resolveStyleArray($value, $state);
        return $this->styleArrayToCss($style, $use_important);
    }

    /**
     * Normalizes a property value into an array regardless of storage format.
     *
     * @param mixed $value Raw stored value.
     * @return array
     */
    protected function normalizeStyleStructure($value)
    {
        if (is_array($value)) {
            return $value;
        }

        if (is_string($value)) {
            $decoded = json_decode($value, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                return $decoded;
            }

            $unserialized = @unserialize($value);
            if (is_array($unserialized)) {
                return $unserialized;
            }
        }

        return array();
    }

    /**
     * Attempts to extract a state/block array from responsive or non-responsive format.
     *
     * @param mixed       $value
     * @param string      $state
     * @param string|null $breakpoint
     * @return array
     */
    protected function resolveStyleArray($value, $state = 'default')
    {
        $style = $this->normalizeStyleStructure($value);

        if ($this->isLegacyBreakpointStructure($style)) {
            $style = $this->extractLegacyBreakpointBlock($style);
        }

        if (empty($style) || !is_array($style)) {
            return array();
        }

        if (isset($style[$state]) && is_array($style[$state])) {
            return $style[$state];
        }

        if (isset($style['default']) && is_array($style['default'])) {
            return $style['default'];
        }

        $first = reset($style);
        return is_array($first) ? $first : array();
    }

    /**
     * Converts a style block array into an inline CSS string.
     *
     * @param array $style
     * @return string
     */
    protected function styleArrayToCss($style, $use_important = false)
    {
        if (!is_array($style) || empty($style)) {
            return '';
        }

        $important = $use_important ? ' !important' : '';
        $css = array();

        $backgroundCss = $this->buildBackgroundCss($style);
        if ($backgroundCss !== '') {
            $css[] = $use_important ? str_replace(';', $important . ';', $backgroundCss) : $backgroundCss;
        }

        foreach (array(
            'background_mode',
            'background-color',
            'gradient_color_1',
            'gradient_color_2',
            'gradient_angle',
            'background-image',
            'background-size',
            'background-repeat',
            'background_image',
            'background_image_size',
            'background_image_position',
            'background_image_repeat'
        ) as $key) {
            if (array_key_exists($key, $style)) {
                unset($style[$key]);
            }
        }

        if (isset($style['padding']) && is_array($style['padding'])) {
            $padding = $this->formatBoxValue($style['padding']);
            if ($padding !== '') {
                $css[] = 'padding: ' . $padding . $important . ';';
            }
            unset($style['padding']);
        }

        if (isset($style['margin']) && is_array($style['margin'])) {
            $margin = $this->formatBoxValue($style['margin']);
            if ($margin !== '') {
                $css[] = 'margin: ' . $margin . $important . ';';
            }
            unset($style['margin']);
        }

        if (isset($style['border']) && is_array($style['border'])) {
            $borderCss = $this->formatBorderCss($style['border']);
            if ($use_important) {
                $borderCss = array_map(function($declaration) use ($important) {
                    return str_replace(';', $important . ';', $declaration);
                }, $borderCss);
            }
            $css = array_merge($css, $borderCss);
            unset($style['border']);
        }

        if (!empty($style['border-radius'])) {
            $css[] = 'border-radius: ' . $style['border-radius'] . $important . ';';
            unset($style['border-radius']);
        }

        if (isset($style['box-shadow']) && is_array($style['box-shadow'])) {
            $boxShadow = $this->formatBoxShadow($style['box-shadow']);
            if ($boxShadow !== '') {
                $css[] = 'box-shadow: ' . $boxShadow . $important . ';';
            }
            unset($style['box-shadow']);
        }

        if (!empty($style['size'])) {
            $size_value = trim((string) $style['size']);
            if ($size_value !== '') {
                $size_declarations = array(
                    'width: ' . $size_value . ';',
                    'height: ' . $size_value . ';',
                    'min-width: ' . $size_value . ';',
                    'min-height: ' . $size_value . ';',
                );
                foreach ($size_declarations as $declaration) {
                    $css[] = $use_important ? str_replace(';', $important . ';', $declaration) : $declaration;
                }
            }
            unset($style['size']);
        }

        if (!empty($style['gap'])) {
            $css[] = 'gap: ' . $style['gap'] . $important . ';';
            unset($style['gap']);
        }

        if (!empty($style['outline'])) {
            $css[] = 'outline: ' . $style['outline'] . $important . ';';
            unset($style['outline']);
        }

        if (!empty($style['accent_color'])) {
            $css[] = 'accent-color: ' . $style['accent_color'] . $important . ';';
            unset($style['accent_color']);
        }

        if (!empty($style['placeholder_color'])) {
            $css[] = '--placeholder-color: ' . $style['placeholder_color'] . $important . ';';
            unset($style['placeholder_color']);
        }

        if (!empty($style['custom_css'])) {
            $custom = $this->normalizeCustomCss($style['custom_css']);
            if ($custom !== '') {
                $css[] = $use_important ? str_replace(';', $important . ';', $custom) : $custom;
            }
            unset($style['custom_css']);
        }

        // Handle font-family with proper quoting and fallbacks
        if (!empty($style['font-family'])) {
            $font_family_css = $this->formatFontFamilyCss($style['font-family']);
            $css[] = $use_important ? str_replace(';', $important . ';', $font_family_css) : $font_family_css;
            unset($style['font-family']);
        }

        foreach ($style as $property => $value) {
            if (is_array($value)) {
                continue;
            }

            $value = trim((string) $value);
            if ($value === '') {
                continue;
            }

            $declaration = $this->formatCssDeclaration($property, $value);
            $css[] = $use_important ? str_replace(';', $important . ';', $declaration) : $declaration;
        }

        $css = array_filter($css);

        return trim(implode(' ', $css));
    }

    /**
     * Builds CSS for background settings supporting single, gradient, and image modes.
     */
    protected function buildBackgroundCss(array $style)
    {
        $mode = isset($style['background_mode']) ? (string) $style['background_mode'] : 'single';

        if ('gradient' === $mode) {
            $color1 = $style['gradient_color_1'] ?? '#ffffff';
            $color2 = $style['gradient_color_2'] ?? '#f0f0f0';
            $angle  = $style['gradient_angle'] ?? '45deg';

            return sprintf('background: linear-gradient(%s, %s, %s);', $angle, $color1, $color2);
        }

        if ('image' === $mode) {
            $image = isset($style['background-image']) ? trim((string) $style['background-image']) : '';
            if ($image === '' && isset($style['background_image'])) {
                $image = trim((string) $style['background_image']);
            }
            if ($image === '') {
                return '';
            }

            $size     = $style['background-size'] ?? ($style['background_image_size'] ?? 'cover');
            $position = $style['background-position'] ?? ($style['background_image_position'] ?? 'center');
            $repeat   = $style['background-repeat'] ?? ($style['background_image_repeat'] ?? 'no-repeat');
            $imageUrl = $this->resolveAssetUrl($image);
            if (function_exists('esc_url')) {
                $imageUrl = \esc_url($imageUrl);
            }

            return sprintf(
                "background-image: url('%s'); background-size: %s; background-position: %s; background-repeat: %s;",
                $imageUrl,
                $size,
                $position,
                $repeat
            );
        }

        $color = $style['background-color'] ?? '';
        return $color !== '' ? 'background-color: ' . $color . ';' : '';
    }

    /**
     * Resolves asset URLs (e.g., background images) to absolute plugin URLs.
     *
     * @param string $path
     * @return string
     */
    protected function resolveAssetUrl($path)
    {
        $path = trim((string) $path);
        if ($path === '') {
            return '';
        }

        // Absolute URLs (including protocol-relative) and data URIs should pass through unchanged.
        if (preg_match('#^(?:[a-z][a-z0-9+.-]*:)?//#i', $path) || strpos($path, 'data:') === 0) {
            return $path;
        }

        $root = '';
        if (isset($this->config) && is_object($this->config) && method_exists($this->config, 'getRootUrl')) {
            $root = rtrim((string) $this->config->getRootUrl(), '/');
        }

        if ($root === '') {
            return $path;
        }

        if (strpos($path, '/') === 0) {
            return $root . $path;
        }

        return $root . '/' . ltrim($path, '/');
    }

    /**
     * Formats CSS for padding or margin arrays.
     */
    protected function formatBoxValue($value)
    {
        if (!is_array($value)) {
            return trim((string) $value);
        }

        $top    = $value['top'] ?? $value['all'] ?? null;
        $right  = $value['right'] ?? $value['all'] ?? null;
        $bottom = $value['bottom'] ?? $value['all'] ?? null;
        $left   = $value['left'] ?? $value['all'] ?? null;

        $values = array($top, $right, $bottom, $left);

        if (count(array_filter($values, function ($item) {
            return $item !== null && $item !== '';
        })) === 0) {
            return '';
        }

        foreach ($values as $index => $item) {
            if ($item === null || $item === '') {
                $values[$index] = '0';
            }
        }

        return implode(' ', $values);
    }

    /**
     * Formats border arrays into width/style/color declarations.
     */
    protected function formatBorderCss(array $border)
    {
        $css = array();

        if (isset($border['width'])) {
            $width = $this->formatBoxValue($border['width']);
            if ($width !== '') {
                $css[] = 'border-width: ' . $width . ';';
            }
        }

        if (!empty($border['style'])) {
            $css[] = 'border-style: ' . $border['style'] . ';';
        }

        if (!empty($border['color'])) {
            $css[] = 'border-color: ' . $border['color'] . ';';
        }

        return $css;
    }

    /**
     * Formats a box shadow array into a CSS value string.
     */
    protected function formatBoxShadow(array $shadow)
    {
        $offsetX = $shadow['offset_x'] ?? '0';
        $offsetY = $shadow['offset_y'] ?? '0';
        $blur    = $shadow['blur'] ?? '0';
        $spread  = $shadow['spread'] ?? '0';
        $color   = $shadow['color'] ?? 'rgba(0,0,0,0)';

        return trim(sprintf('%s %s %s %s %s', $offsetX, $offsetY, $blur, $spread, $color));
    }

    /**
     * Normalizes arbitrary custom CSS to ensure declarations end with semicolons.
     */
    protected function normalizeCustomCss($css)
    {
        $css = trim((string) $css);
        if ($css === '') {
            return '';
        }

        $segments = preg_split('/;/', $css);
        $normalized = array();

        foreach ($segments as $segment) {
            $segment = trim($segment);
            if ($segment === '') {
                continue;
            }
            if (strpos($segment, ':') === false) {
                continue;
            }
            $normalized[] = $segment . ';';
        }

        return implode(' ', $normalized);
    }

    /**
     * Builds a single CSS declaration string from a property/value pair.
     */
    protected function formatCssDeclaration($property, $value)
    {
        // Map internal property names to CSS property names
        $property_map = array(
            'style' => 'border-style',
        );

        $css_property = isset($property_map[$property]) ? $property_map[$property] : $property;
        return $css_property . ': ' . $value . ';';
    }

    /**
     * Formats font-family CSS with proper quoting and intelligent fallbacks.
     * Quotes multi-word font names and adds appropriate generic font family fallbacks.
     *
     * Examples:
     * - "Libre Franklin" -> "Libre Franklin", sans-serif
     * - "Open Sans" -> "Open Sans", sans-serif
     * - "Georgia" -> "Georgia", serif
     * - "Roboto Mono" -> "Roboto Mono", monospace
     *
     * @param string $font_family The font family name to format
     * @return string CSS declaration string
     */
    protected function formatFontFamilyCss($font_family)
    {
        $font_family = trim((string) $font_family);

        if ($font_family === '' || $font_family === 'inherit') {
            return 'font-family: inherit;';
        }

        // Check if font is already quoted by sanitizer
        $is_already_quoted = ($font_family[0] === '"' && $font_family[strlen($font_family) - 1] === '"');

        // Extract the unquoted font name for lookups
        $font_name = $is_already_quoted ? trim($font_family, '"') : $font_family;

        // Initialize FontRegistry to check font type
        $registry = new \GriffinForms\Admin\Html\FormControls\FormTheme\FontRegistry();

        // Determine if it's a monospace or serif font
        $monospace_fonts = array('Roboto Mono', 'JetBrains Mono');
        $serif_fonts = array('Georgia', 'Merriweather', 'Playfair Display');

        // Use pre-quoted version if available, otherwise quote if needed
        if ($is_already_quoted) {
            $quoted_font = $font_family;
        } else {
            $quoted_font = (strpos($font_name, ' ') !== false) ? '"' . $font_name . '"' : $font_name;
        }

        // Add appropriate fallback based on unquoted name
        if (in_array($font_name, $monospace_fonts, true)) {
            $fallback = 'monospace';
        } elseif (in_array($font_name, $serif_fonts, true)) {
            $fallback = 'serif';
        } else {
            // Default fallback for sans-serif fonts
            $fallback = 'sans-serif';
        }

        return 'font-family: ' . $quoted_font . ', ' . $fallback . ';';
    }

    /**
     * Populates the theme instance using an existing database row or array.
     *
     * @param object|array $data Theme data.
     * @return void
     */
    public function hydrateFromSource($data)
    {
        if (empty($data)) {
            return;
        }

        $source = is_object($data) ? get_object_vars($data) : (array) $data;

        foreach ($source as $prop => $value) {
            if (!property_exists($this, $prop)) {
                continue;
            }

            $decoded = $this->maybeDecode($value);

            if ($this->shouldFlattenLegacyProperty($prop)) {
                $decoded = $this->flattenLegacyResponsiveProperty($decoded);
            }

            $this->$prop = $decoded;
        }
    }

    /**
     * Override loadItem to support legacy format migration
     */
    public function loadItem($id)
    {
        return parent::loadItem(absint($id));
    }

    /**
     * Determines if a property should be flattened from legacy responsive format.
     */
    protected function shouldFlattenLegacyProperty($property)
    {
        return in_array($property, array('form_style', 'page_style', 'submit_button', 'previous_button', 'next_button'), true);
    }

    /**
     * Converts legacy responsive property values to the new flat structure.
     *
     * @param mixed $value
     * @return array
     */
    protected function flattenLegacyResponsiveProperty($value)
    {
        $structure = $this->normalizeStyleStructure($value);

        if (!$this->isLegacyBreakpointStructure($structure)) {
            return $structure;
        }

        return $this->extractLegacyBreakpointBlock($structure);
    }

    /**
     * Detects whether the provided structure is using legacy breakpoint keys.
     *
     * @param mixed $structure
     * @return bool
     */
    protected function isLegacyBreakpointStructure($structure)
    {
        if (!is_array($structure) || empty($structure)) {
            return false;
        }

        $breakpoints = array('xs', 'sm', 'md', 'lg');
        $hasBreakpointKey = false;

        foreach ($structure as $key => $value) {
            if (!in_array($key, $breakpoints, true)) {
                return false;
            }
            $hasBreakpointKey = true;
        }

        return $hasBreakpointKey;
    }

    /**
     * Extracts the first meaningful breakpoint block, preferring xs.
     *
     * @param array $structure
     * @return array
     */
    protected function extractLegacyBreakpointBlock(array $structure)
    {
        foreach (array('xs', 'sm', 'md', 'lg') as $breakpoint) {
            if (isset($structure[$breakpoint]) && $this->styleBlockHasValues($structure[$breakpoint])) {
                return is_array($structure[$breakpoint]) ? $structure[$breakpoint] : array();
            }
        }

        $first = reset($structure);
        return is_array($first) ? $first : array();
    }

    /**
     * Determines if a style block contains any meaningful values.
     *
     * @param mixed $value
     * @return bool
     */
    protected function styleBlockHasValues($value)
    {
        if (is_array($value)) {
            foreach ($value as $item) {
                if ($this->styleBlockHasValues($item)) {
                    return true;
                }
            }
            return false;
        }

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