<?php

namespace AIPT\Api\BulkGenerator\FAQ;

/**
 * FAQ display settings management class.
 *
 * Handles CRUD operations for FAQ display settings stored in wp_options.
 * Settings are stored as a flat array under a single option key.
 *
 * @package AIPT\Api\BulkGenerator\FAQ
 * @since 2.5.0
 */
class FAQDisplaySettings {

    /**
     * Option key for FAQ display settings.
     */
    private const OPTION_KEY = 'aipt_faq_display_settings';

    /**
     * Valid theme presets.
     */
    private const VALID_THEME_PRESETS = ['default', 'minimal', 'bordered', 'dark', 'gradient', 'elegant'];

    /**
     * Valid toggle icons.
     */
    private const VALID_TOGGLE_ICONS = ['chevron', 'plus', 'arrow', 'caret'];

    /**
     * Valid animation types.
     */
    private const VALID_ANIMATIONS = ['none', 'fade', 'slide', 'smooth'];

    /**
     * Valid display modes.
     */
    private const VALID_DISPLAY_MODES = ['auto', 'shortcode'];

    /**
     * Valid section names for getSection().
     */
    private const VALID_SECTIONS = [
        'general',
        'display',
        'container',
        'colors',
        'typography',
        'spacing',
        'icons',
        'advanced',
    ];

    /**
     * Cached settings.
     *
     * @var array|null
     */
    private $cached_settings = null;

    /**
     * Get default settings.
     *
     * Returns the canonical default values for all FAQ display settings fields.
     *
     * @return array Default values for all settings fields.
     */
    public function getDefaults(): array {
        return [
            'accordion_enabled'        => true,
            'theme_preset'             => 'default',
            'qa_labels_enabled'        => true,
            'question_label'           => 'Q',
            'answer_label'             => 'A',
            'display_mode'             => 'auto',
            'container_class_name'     => '',
            'question_color'           => '',
            'answer_color'             => '',
            'background_color'         => '',
            'border_color'             => '',
            'font_family'              => '',
            'font_size'                => '16px',
            'font_weight'              => 'normal',
            'padding_top'              => '20px',
            'padding_right'            => '20px',
            'padding_bottom'           => '20px',
            'padding_left'             => '20px',
            'margin_top'               => '0',
            'margin_right'             => '0',
            'margin_bottom'            => '0',
            'margin_left'              => '0',
            'gap'                      => '10px',
            'border_radius_top_left'   => '8px',
            'border_radius_top_right'  => '8px',
            'border_radius_bottom_right' => '8px',
            'border_radius_bottom_left'  => '8px',
            'border_width_top'         => '1px',
            'border_width_right'       => '1px',
            'border_width_bottom'      => '1px',
            'border_width_left'        => '1px',
            'toggle_icon'              => 'chevron',
            'animation'                => 'smooth',
            'custom_css'               => '',
            'schema_enabled'           => false,
        ];
    }

    /**
     * Get all settings, merging stored values with defaults.
     *
     * @return array Complete settings array with defaults applied for missing keys.
     */
    public function getAll(): array {
        if ( $this->cached_settings !== null ) {
            return $this->cached_settings;
        }

        $raw = (array) wp_parse_args( get_option( self::OPTION_KEY, [] ), $this->getDefaults() );

        $raw = $this->migrateToPerSide( $raw );

        $this->cached_settings = $raw;

        return $this->cached_settings;
    }

    /**
     * Get a specific setting value by key.
     *
     * @param string $key     The setting key to retrieve.
     * @param mixed  $default Default value if the key does not exist.
     * @return mixed The setting value, or $default if the key is not found.
     */
    public function get( string $key, $default = null ) {
        $settings = $this->getAll();

        if ( array_key_exists( $key, $settings ) ) {
            return $settings[ $key ];
        }

        return $default;
    }

    /**
     * Update settings.
     *
     * Sanitizes the incoming settings array and persists it to wp_options
     * with autoload disabled for performance.
     *
     * @param array $settings Settings fields to update (full or partial).
     * @return bool True if the option was updated successfully, false otherwise.
     */
    public function update( array $settings ): bool {

        $current   = $this->cached_settings ?? wp_parse_args( get_option( self::OPTION_KEY, [] ), $this->getDefaults() );
        $merged    = array_merge( $current, $settings );
        $sanitized = $this->sanitize( $merged );
        $result    = update_option( self::OPTION_KEY, $sanitized, false );

        if ( $result ) {
            $this->cached_settings = $sanitized;
        }

        return $result;
    }

    /**
     * Sanitize settings input.
     *
     * Validates and sanitizes every field according to its expected type and
     * allowed values. Unknown keys are stripped. Missing keys fall back to
     * their defaults.
     *
     * @param array $input Raw input to sanitize.
     * @return array Sanitized settings array.
     */
    public function sanitize( array $input ): array {
        $defaults  = $this->getDefaults();
        $sanitized = [];

        $sanitized['accordion_enabled']       = (bool) ( $input['accordion_enabled'] ?? $defaults['accordion_enabled'] );
        $sanitized['qa_labels_enabled']        = (bool) ( $input['qa_labels_enabled'] ?? $defaults['qa_labels_enabled'] );
        $sanitized['schema_enabled']          = (bool) ( $input['schema_enabled'] ?? $defaults['schema_enabled'] );

        $theme_preset              = sanitize_text_field( (string) ( $input['theme_preset'] ?? $defaults['theme_preset'] ) );
        $sanitized['theme_preset'] = in_array( $theme_preset, self::VALID_THEME_PRESETS, true )
            ? $theme_preset
            : $defaults['theme_preset'];

        $toggle_icon              = sanitize_text_field( (string) ( $input['toggle_icon'] ?? $defaults['toggle_icon'] ) );
        $sanitized['toggle_icon'] = in_array( $toggle_icon, self::VALID_TOGGLE_ICONS, true )
            ? $toggle_icon
            : $defaults['toggle_icon'];

        $animation              = sanitize_text_field( (string) ( $input['animation'] ?? $defaults['animation'] ) );
        $sanitized['animation'] = in_array( $animation, self::VALID_ANIMATIONS, true )
            ? $animation
            : $defaults['animation'];

        $display_mode              = sanitize_text_field( (string) ( $input['display_mode'] ?? $defaults['display_mode'] ) );
        $sanitized['display_mode'] = in_array( $display_mode, self::VALID_DISPLAY_MODES, true )
            ? $display_mode
            : $defaults['display_mode'];

        $color_fields = [ 'question_color', 'answer_color', 'background_color', 'border_color' ];
        foreach ( $color_fields as $field ) {
            $sanitized[ $field ] = $this->sanitizeHexColor( (string) ( $input[ $field ] ?? $defaults[ $field ] ) );
        }

        $dimension_fields = [
            'font_size', 'gap',
            'padding_top', 'padding_right', 'padding_bottom', 'padding_left',
            'margin_top', 'margin_right', 'margin_bottom', 'margin_left',
            'border_radius_top_left', 'border_radius_top_right', 'border_radius_bottom_right', 'border_radius_bottom_left',
            'border_width_top', 'border_width_right', 'border_width_bottom', 'border_width_left',
        ];
        foreach ( $dimension_fields as $field ) {
            $sanitized[ $field ] = $this->sanitizeCssDimension(
                (string) ( $input[ $field ] ?? $defaults[ $field ] ),
                (string) $defaults[ $field ]
            );
        }

        $sanitized['font_family']    = sanitize_text_field( (string) ( $input['font_family'] ?? $defaults['font_family'] ) );
        $sanitized['font_weight']    = sanitize_text_field( (string) ( $input['font_weight'] ?? $defaults['font_weight'] ) );
        $sanitized['question_label'] = mb_substr( sanitize_text_field( (string) ( $input['question_label'] ?? $defaults['question_label'] ) ), 0, 20 );
        $sanitized['answer_label']   = mb_substr( sanitize_text_field( (string) ( $input['answer_label'] ?? $defaults['answer_label'] ) ), 0, 20 );

        $raw_class = (string) ( $input['container_class_name'] ?? $defaults['container_class_name'] );
        if ( '' === trim( $raw_class ) ) {
            $sanitized['container_class_name'] = '';
        } else {
            $parts = preg_split( '/\s+/', trim( $raw_class ), -1, PREG_SPLIT_NO_EMPTY );
            $parts = is_array( $parts ) ? $parts : [];
            $sanitized['container_class_name'] = implode( ' ', array_map( 'sanitize_html_class', $parts ) );
        }

        $raw_css               = wp_strip_all_tags( (string) ( $input['custom_css'] ?? $defaults['custom_css'] ) );
        $sanitized['custom_css'] = mb_substr( $raw_css, 0, 10240 );

        return $sanitized;
    }

    /**
     * Get settings grouped into a named section.
     *
     * @param string $section Section name (general, container, colors, typography,
     *                         spacing, icons, advanced, woocommerce).
     * @return array Associative array of settings belonging to the section,
     *               or an empty array for an unknown section name.
     */
    public function getSection( string $section ): array {
        if ( ! in_array( $section, self::VALID_SECTIONS, true ) ) {
            return [];
        }

        $settings = $this->getAll();

        $map = [
            'general'      => [ 'accordion_enabled', 'theme_preset', 'qa_labels_enabled', 'question_label', 'answer_label' ],
            'display'      => [ 'display_mode' ],
            'container'    => [ 'container_class_name' ],
            'colors'       => [ 'question_color', 'answer_color', 'background_color', 'border_color' ],
            'typography'   => [ 'font_family', 'font_size', 'font_weight' ],
            'spacing'      => [
                'padding_top', 'padding_right', 'padding_bottom', 'padding_left',
                'margin_top', 'margin_right', 'margin_bottom', 'margin_left',
                'gap',
                'border_radius_top_left', 'border_radius_top_right', 'border_radius_bottom_right', 'border_radius_bottom_left',
                'border_width_top', 'border_width_right', 'border_width_bottom', 'border_width_left',
            ],
            'icons'        => [ 'toggle_icon', 'animation' ],
            'advanced'     => [ 'custom_css', 'schema_enabled' ],
        ];

        $keys   = $map[ $section ] ?? [];
        $result = [];

        foreach ( $keys as $key ) {
            if ( array_key_exists( $key, $settings ) ) {
                $result[ $key ] = $settings[ $key ];
            }
        }

        return $result;
    }

    /**
     * Clear the cached settings.
     *
     * Should be called after external modifications to the underlying option.
     *
     * @return void
     */
    public function clearCache(): void {
        $this->cached_settings = null;
    }

    /**
     * Migrate legacy single-value spacing/border fields to per-side fields.
     *
     * Handles backward compatibility when stored settings still use the old
     * single-value format (e.g. 'padding' => '20px' instead of 'padding_top' => '20px').
     *
     * @param array $settings Settings array to migrate.
     * @return array Settings with per-side fields populated.
     */
    private function migrateToPerSide( array $settings ): array {

        $side_fields = [ 'padding', 'margin', 'border_width' ];
        foreach ( $side_fields as $field ) {
            if ( isset( $settings[ $field ] ) && is_string( $settings[ $field ] ) ) {
                $value = $settings[ $field ];
                foreach ( [ 'top', 'right', 'bottom', 'left' ] as $side ) {
                    $key = $field . '_' . $side;
                    if ( ! isset( $settings[ $key ] ) || $settings[ $key ] === $this->getDefaults()[ $key ] ) {
                        $settings[ $key ] = $value;
                    }
                }
                unset( $settings[ $field ] );
            }
        }

        if ( isset( $settings['border_radius'] ) && is_string( $settings['border_radius'] ) ) {
            $value   = $settings['border_radius'];
            $corners = [ 'top_left', 'top_right', 'bottom_right', 'bottom_left' ];
            foreach ( $corners as $corner ) {
                $key = 'border_radius_' . $corner;
                if ( ! isset( $settings[ $key ] ) || $settings[ $key ] === $this->getDefaults()[ $key ] ) {
                    $settings[ $key ] = $value;
                }
            }
            unset( $settings['border_radius'] );
        }

        return $settings;
    }

    /**
     * Sanitize a hex color value.
     *
     * Accepts both 3-character and 6-character hex codes prefixed with '#'.
     * Returns an empty string for invalid or empty input.
     *
     * @param string $color Raw color value.
     * @return string Valid hex color string or empty string.
     */
    private function sanitizeHexColor( string $color ): string {
        $color = trim( $color );

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

        if ( preg_match( '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/', $color ) ) {
            return strtolower( $color );
        }

        return '';
    }

    /**
     * Sanitize a CSS dimension value (e.g. "16px", "1.5em", "0").
     *
     * Allows an empty string (interpreted as "inherit") or a numeric value
     * followed by a recognised unit. Falls back to $default on invalid input.
     *
     * @param string $value   Raw dimension value from user input.
     * @param string $default Fallback value to use when $value is invalid.
     * @return string Sanitized CSS dimension or $default.
     */
    private function sanitizeCssDimension( string $value, string $default ): string {
        $value = sanitize_text_field( $value );

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

        if ( '0' === $value ) {
            return '0';
        }

        if ( preg_match( '/^\d+(\.\d+)?(px|em|rem|%|vh|vw)$/', $value ) ) {
            return $value;
        }

        return $default;
    }
}
