<?php
/**
 * Appearance Presets REST API Endpoint.
 *
 * @package BoostBot
 * @since   1.3.0
 */

// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
 * Class BoostBot_Appearance_Presets_Endpoint
 *
 * Handles REST API endpoints for appearance presets management.
 *
 * @since 1.3.0
 */
class BoostBot_Appearance_Presets_Endpoint extends BoostBot_Base_Endpoint {

    /**
     * REST route base.
     *
     * @var string
     */
    protected $rest_base = 'appearance-presets';

    /**
     * Register routes.
     *
     * @return void
     */
    public function register() {
        // GET /appearance-presets - List all presets
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base,
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'get_presets' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // GET /appearance-presets/dropdown - Get for dropdown
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/dropdown',
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'get_dropdown' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // POST /appearance-presets - Create preset
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base,
            array(
                'methods'             => 'POST',
                'callback'            => array( $this, 'create_preset' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // /appearance-presets/{id} - GET, PUT, DELETE
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)',
            array(
                array(
                    'methods'             => 'GET',
                    'callback'            => array( $this, 'get_preset' ),
                    'permission_callback' => array( $this, 'admin_access' ),
                ),
                array(
                    'methods'             => 'PUT',
                    'callback'            => array( $this, 'update_preset' ),
                    'permission_callback' => array( $this, 'admin_access' ),
                ),
                array(
                    'methods'             => 'DELETE',
                    'callback'            => array( $this, 'delete_preset' ),
                    'permission_callback' => array( $this, 'admin_access' ),
                ),
            )
        );

        // PUT /appearance-presets/{id}/default - Set as default
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)/default',
            array(
                'methods'             => 'PUT',
                'callback'            => array( $this, 'set_default' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // GET /appearance-presets/{id}/linked-bots - Get bots using this preset
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)/linked-bots',
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'get_linked_bots' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // GET /appearance-presets/global-values - Get global appearance values
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/global-values',
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'get_global_values' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // PUT /appearance-presets/global-values - Update global appearance values
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/global-values',
            array(
                'methods'             => 'PUT',
                'callback'            => array( $this, 'update_global_values' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // GET /appearance-presets/import-elementor - Import from Elementor
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/import-elementor',
            array(
                'methods'             => 'GET',
                'callback'            => array( $this, 'import_from_elementor' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // POST /appearance-presets/bulk-delete - Bulk delete presets
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/bulk-delete',
            array(
                'methods'             => 'POST',
                'callback'            => array( $this, 'bulk_delete' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // POST /appearance-presets/{id}/link-bot/{bot_id} - Link bot to this preset
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)/link-bot/(?P<bot_id>\d+)',
            array(
                'methods'             => 'POST',
                'callback'            => array( $this, 'link_bot' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // POST /appearance-presets/{id}/unlink-bot/{bot_id} - Unlink bot from this preset
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)/unlink-bot/(?P<bot_id>\d+)',
            array(
                'methods'             => 'POST',
                'callback'            => array( $this, 'unlink_bot' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );

        // POST /appearance-presets/{id}/duplicate - Duplicate a preset
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>\d+)/duplicate',
            array(
                'methods'             => 'POST',
                'callback'            => array( $this, 'duplicate_preset' ),
                'permission_callback' => array( $this, 'admin_access' ),
            )
        );
    }

    /**
     * Get all presets.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_presets( $request ) {
        $model = new BoostBot_Appearance_Preset_Model();

        $args = array( 'orderby' => 'name', 'order' => 'ASC' );

        if ( $request->has_param( 'language' ) ) {
            $args['where'] = array( 'language' => $request->get_param( 'language' ) );
        }

        $presets = $model->get_all( $args );

        return $this->success( array( 'presets' => $presets ) );
    }

    /**
     * Get presets for dropdown.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_dropdown( $request ) {
        $model = new BoostBot_Appearance_Preset_Model();
        $presets = $model->get_for_dropdown();

        return $this->success( array( 'presets' => $presets ) );
    }

    /**
     * Get single preset.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_preset( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        $response = array( 'preset' => $preset );

        // Include custom font CSS if a custom font is set in the preset's global_values
        // Handle both array and object access for global_values
        $global_values = is_array( $preset ) ? ( $preset['global_values'] ?? null ) : ( $preset->global_values ?? null );
        if ( $global_values ) {
            $typography = is_array( $global_values ) ? ( $global_values['typography'] ?? null ) : ( $global_values->typography ?? null );
            if ( $typography ) {
                $font_family = is_array( $typography ) ? ( $typography['fontFamily'] ?? null ) : ( $typography->fontFamily ?? null );
                if ( ! empty( $font_family ) ) {
                    $custom_font_css = $this->get_elementor_custom_font_css( $font_family );
                    if ( $custom_font_css ) {
                        $response['custom_font_css'] = $custom_font_css;
                    }
                }
            }
        }

        return $this->success( $response );
    }

    /**
     * Create preset.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function create_preset( $request ) {
        $model = new BoostBot_Appearance_Preset_Model();
        $data = $request->get_json_params();

        // Generate slug if not provided
        if ( empty( $data['slug'] ) && ! empty( $data['name'] ) ) {
            $data['slug'] = $model->generate_slug( $data['name'] );
        }

        // Validate
        $validation = $model->validate( $data );
        if ( is_wp_error( $validation ) ) {
            return $this->error( $validation->get_error_message() );
        }

        // Sanitize
        $sanitized = $model->sanitize( $data );

        // Create
        $id = $model->create( $sanitized );

        if ( ! $id ) {
            return $this->error( __( 'Failed to create appearance preset.', 'boostbot' ) );
        }

        $preset = $model->get( $id );

        return $this->success( array(
            'preset'  => $preset,
            'message' => __( 'Appearance preset created successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Update preset.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function update_preset( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        $data = $request->get_json_params();

        // Validate
        $validation = $model->validate( $data );
        if ( is_wp_error( $validation ) ) {
            return $this->error( $validation->get_error_message() );
        }

        // Sanitize
        $sanitized = $model->sanitize( $data );

        // Don't allow changing system presets to non-system
        if ( $preset->is_system && isset( $sanitized['is_system'] ) && ! $sanitized['is_system'] ) {
            unset( $sanitized['is_system'] );
        }

        // Update
        $result = $model->update( $id, $sanitized );

        if ( ! $result ) {
            return $this->error( __( 'Failed to update appearance preset.', 'boostbot' ) );
        }

        $preset = $model->get( $id );

        return $this->success( array(
            'preset'  => $preset,
            'message' => __( 'Appearance preset updated successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Delete preset.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function delete_preset( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        // Don't allow deleting system presets
        if ( $preset->is_system ) {
            return $this->error( __( 'Cannot delete system presets.', 'boostbot' ) );
        }

        // Check if any bots use this preset
        $linked_bots = $model->get_linked_bots( $id );
        if ( ! empty( $linked_bots ) ) {
            return $this->error( sprintf(
                /* translators: %d: number of bots */
                __( 'Cannot delete: %d bot(s) are using this preset.', 'boostbot' ),
                count( $linked_bots )
            ) );
        }

        // Delete
        $result = $model->delete( $id );

        if ( ! $result ) {
            return $this->error( __( 'Failed to delete appearance preset.', 'boostbot' ) );
        }

        return $this->success( array(
            'message' => __( 'Appearance preset deleted successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Set preset as default.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function set_default( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        $result = $model->set_default( $id );

        if ( ! $result ) {
            return $this->error( __( 'Failed to set as default.', 'boostbot' ) );
        }

        return $this->success( array(
            'message' => __( 'Preset set as default successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Get bots using this preset.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_linked_bots( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        $bots = $model->get_linked_bots( $id );

        return $this->success( array( 'bots' => $bots ) );
    }

    /**
     * Get global appearance values.
     *
     * Per-language scoped: reads from `boostbot_appearance_global_values_{lang}`.
     * Falls back to the legacy unsuffixed option for backwards compatibility.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function get_global_values( $request ) {
        $language = $this->get_request_language( $request );

        $global_values = array();
        if ( $language && 'all' !== $language ) {
            $global_values = get_option( 'boostbot_appearance_global_values_' . $language, array() );
        }

        // Fallback to legacy unsuffixed option
        if ( empty( $global_values ) ) {
            $global_values = get_option( 'boostbot_appearance_global_values', array() );
        }

        // Return default structure if still empty
        if ( empty( $global_values ) ) {
            $global_values = $this->get_default_global_values();
        }

        $response = array( 'global_values' => $global_values );

        // Include custom font CSS if a custom font is set
        if ( ! empty( $global_values['typography']['fontFamily'] ) ) {
            $custom_font_css = $this->get_elementor_custom_font_css( $global_values['typography']['fontFamily'] );
            if ( $custom_font_css ) {
                $response['custom_font_css'] = $custom_font_css;
            }
        }

        return $this->success( $response );
    }

    /**
     * Update global appearance values.
     *
     * Per-language scoped: writes to `boostbot_appearance_global_values_{lang}`.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function update_global_values( $request ) {
        $data = $request->get_json_params();

        if ( empty( $data ) ) {
            return $this->error( __( 'No data provided.', 'boostbot' ) );
        }

        $language = $this->get_request_language( $request );

        // Guard against 'all' — fall back to primary language for writes.
        if ( ! $language || 'all' === $language ) {
            $language = BoostBot_Settings::get( 'primary_language', 'he' );
        }

        // Sanitize the data
        $sanitized = $this->sanitize_global_values( $data );

        $option_key = 'boostbot_appearance_global_values_' . $language;

        // Store as JSON in options
        $result = update_option( $option_key, $sanitized );

        if ( false === $result ) {
            // Check if value is unchanged (update_option returns false when value doesn't change)
            $current = get_option( $option_key, array() );
            if ( $current === $sanitized ) {
                return $this->success( array(
                    'message'       => __( 'Global values saved (unchanged).', 'boostbot' ),
                    'global_values' => $sanitized,
                ) );
            }
            return $this->error( __( 'Failed to save global values.', 'boostbot' ) );
        }

        return $this->success( array(
            'message'       => __( 'Global values saved successfully.', 'boostbot' ),
            'global_values' => $sanitized,
        ) );
    }

    /**
     * Get default global values structure.
     *
     * @return array Default global values.
     */
    private function get_default_global_values() {
        return array(
            'colors'     => array(
                'primary'        => '#3BC2D0',
                'secondary'      => '#E32774',
                'accent'         => '#6366F1',
                'background'     => '#FFFFFF',
                'backgroundDark' => '#1F2937',
                'surface'        => '#F9FAFB',
                'text'           => '#1F2937',
                'textLight'      => '#FFFFFF',
                'textMuted'      => '#6B7280',
                'border'         => '#E5E7EB',
                'success'        => '#10B981',
                'error'          => '#EF4444',
                'warning'        => '#F59E0B',
                'customColors'   => array(),
            ),
            'typography' => array(
                'fontFamily'  => 'inherit',
                'headingScale' => array(
                    'fontSize'   => '18px',
                    'fontWeight' => '600',
                    'lineHeight' => '1.4',
                ),
                'bodyScale'   => array(
                    'fontSize'   => '14px',
                    'fontWeight' => '400',
                    'lineHeight' => '1.5',
                ),
                'smallScale'  => array(
                    'fontSize'   => '12px',
                    'fontWeight' => '400',
                    'lineHeight' => '1.4',
                ),
                'buttonScale' => array(
                    'fontSize'   => '14px',
                    'fontWeight' => '500',
                    'lineHeight' => '1',
                ),
            ),
        );
    }

    /**
     * Sanitize global values data.
     *
     * @param array $data The data to sanitize.
     * @return array Sanitized data.
     */
    private function sanitize_global_values( $data ) {
        return $this->sanitize_recursive( $data );
    }

    /**
     * Recursively sanitize an array or value.
     *
     * @param mixed $data The data to sanitize.
     * @return mixed Sanitized data.
     */
    private function sanitize_recursive( $data ) {
        if ( is_string( $data ) ) {
            return sanitize_text_field( $data );
        }

        if ( is_array( $data ) ) {
            $sanitized = array();
            foreach ( $data as $key => $value ) {
                // Keep original key - don't use sanitize_key as it lowercases camelCase
                $sanitized[ $key ] = $this->sanitize_recursive( $value );
            }
            return $sanitized;
        }

        // Keep booleans, integers, floats as-is
        if ( is_bool( $data ) || is_int( $data ) || is_float( $data ) ) {
            return $data;
        }

        return $data;
    }

    /**
     * Import global values from Elementor.
     *
     * Reads Elementor's kit settings and converts them to our format.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response The response.
     */
    public function import_from_elementor( $request ) {
        // Check if Elementor is active
        if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
            return $this->error( __( 'Elementor is not active on this site.', 'boostbot' ) );
        }

        // Get Elementor's active kit ID
        $kit_id = get_option( 'elementor_active_kit' );
        if ( ! $kit_id ) {
            return $this->error( __( 'Could not find Elementor kit settings.', 'boostbot' ) );
        }

        // Get kit settings
        $kit_settings = get_post_meta( $kit_id, '_elementor_page_settings', true );
        if ( empty( $kit_settings ) ) {
            return $this->error( __( 'Elementor kit settings are empty.', 'boostbot' ) );
        }

        $typography = $this->extract_elementor_typography( $kit_settings );
        
        $imported = array(
            'colors'     => $this->extract_elementor_colors( $kit_settings ),
            'typography' => $typography,
        );
        
        // Get custom font CSS if font is from Elementor custom fonts
        if ( ! empty( $typography['fontFamily'] ) && 'inherit' !== $typography['fontFamily'] ) {
            $custom_font_css = $this->get_elementor_custom_font_css( $typography['fontFamily'] );
            if ( $custom_font_css ) {
                $imported['customFontCss'] = $custom_font_css;
            }
        }

        return $this->success( array(
            'message'  => __( 'Elementor settings imported successfully!', 'boostbot' ),
            'imported' => $imported,
            'raw_kit'  => $kit_settings, // For debugging
        ) );
    }

    /**
     * Extract colors from Elementor kit settings.
     *
     * @param array $kit_settings Elementor kit settings.
     * @return array Formatted colors.
     */
    private function extract_elementor_colors( $kit_settings ) {
        $colors = array();

        // System colors (primary, secondary, text, accent)
        if ( ! empty( $kit_settings['system_colors'] ) ) {
            foreach ( $kit_settings['system_colors'] as $color ) {
                $id    = sanitize_key( $color['_id'] ?? '' );
                $value = sanitize_hex_color( $color['color'] ?? '' );
                $title = sanitize_text_field( $color['title'] ?? $id );

                if ( $value ) {
                    // Map Elementor color IDs to our system
                    switch ( $id ) {
                        case 'primary':
                            $colors['primary'] = $value;
                            break;
                        case 'secondary':
                            $colors['secondary'] = $value;
                            break;
                        case 'text':
                            $colors['text'] = $value;
                            break;
                        case 'accent':
                            $colors['accent'] = $value;
                            break;
                        default:
                            // Add as custom color
                            if ( ! isset( $colors['customColors'] ) ) {
                                $colors['customColors'] = array();
                            }
                            $colors['customColors'][] = array(
                                'name'  => $title,
                                'value' => $value,
                            );
                            break;
                    }
                }
            }
        }

        // Custom colors
        if ( ! empty( $kit_settings['custom_colors'] ) ) {
            if ( ! isset( $colors['customColors'] ) ) {
                $colors['customColors'] = array();
            }
            foreach ( $kit_settings['custom_colors'] as $color ) {
                $value = sanitize_hex_color( $color['color'] ?? '' );
                $title = sanitize_text_field( $color['title'] ?? 'Custom' );
                if ( $value ) {
                    $colors['customColors'][] = array(
                        'name'  => $title,
                        'value' => $value,
                    );
                }
            }
        }

        return $colors;
    }

    /**
     * Extract typography from Elementor kit settings.
     *
     * @param array $kit_settings Elementor kit settings.
     * @return array Formatted typography.
     */
    private function extract_elementor_typography( $kit_settings ) {
        $typography = array(
            'fontFamily'   => 'inherit',
            'customScales' => array(),
        );

        // Get default font from Elementor - PRIORITY ORDER:
        // 1. FIRST check system_typography (Primary font) - this is the main font users set
        if ( ! empty( $kit_settings['system_typography'] ) && is_array( $kit_settings['system_typography'] ) ) {
            foreach ( $kit_settings['system_typography'] as $sys_typo ) {
                // Look for the primary font first
                if ( isset( $sys_typo['_id'] ) && 'primary' === $sys_typo['_id'] ) {
                    if ( ! empty( $sys_typo['typography_font_family'] ) ) {
                        $typography['fontFamily'] = sanitize_text_field( $sys_typo['typography_font_family'] );
                        break;
                    }
                }
            }
            // If no primary found, use first available font
            if ( 'inherit' === $typography['fontFamily'] ) {
                foreach ( $kit_settings['system_typography'] as $sys_typo ) {
                    if ( ! empty( $sys_typo['typography_font_family'] ) ) {
                        $typography['fontFamily'] = sanitize_text_field( $sys_typo['typography_font_family'] );
                        break;
                    }
                }
            }
        }
        
        // 2. Check body_typography_font_family (Theme Style > Typography > Body)
        if ( ! empty( $kit_settings['body_typography_font_family'] ) && 'inherit' === $typography['fontFamily'] ) {
            $typography['fontFamily'] = sanitize_text_field( $kit_settings['body_typography_font_family'] );
        }
        
        // 3. Check custom_typography if exists (Custom fonts section)
        if ( ! empty( $kit_settings['custom_typography'] ) && is_array( $kit_settings['custom_typography'] ) ) {
            foreach ( $kit_settings['custom_typography'] as $custom_font ) {
                if ( ! empty( $custom_font['typography_font_family'] ) && 'inherit' === $typography['fontFamily'] ) {
                    $typography['fontFamily'] = sanitize_text_field( $custom_font['typography_font_family'] );
                    break;
                }
            }
        }
        
        // 4. LAST - Check default_generic_fonts (This is just the fallback font!)
        if ( ! empty( $kit_settings['default_generic_fonts'] ) && 'inherit' === $typography['fontFamily'] ) {
            $typography['fontFamily'] = sanitize_text_field( $kit_settings['default_generic_fonts'] );
        }

        // System typography (primary, secondary, text, accent)
        if ( ! empty( $kit_settings['system_typography'] ) ) {
            foreach ( $kit_settings['system_typography'] as $typo ) {
                $id = sanitize_key( $typo['_id'] ?? '' );
                
                // Extract all typography properties
                $scale = $this->extract_full_typography_scale( $typo );
                $scale['name'] = sanitize_text_field( $typo['title'] ?? $id );

                // Use first font family as default if not set
                if ( 'inherit' === $typography['fontFamily'] && ! empty( $scale['fontFamily'] ) && 'inherit' !== $scale['fontFamily'] ) {
                    $typography['fontFamily'] = $scale['fontFamily'];
                }

                // Map to our scale system (primary=heading, secondary=body, text=small, accent=button)
                switch ( $id ) {
                    case 'primary':
                        $typography['headingScale'] = $scale;
                        break;
                    case 'secondary':
                        $typography['bodyScale'] = $scale;
                        break;
                    case 'text':
                        $typography['smallScale'] = $scale;
                        break;
                    case 'accent':
                        $typography['buttonScale'] = $scale;
                        break;
                    default:
                        $typography['customScales'][] = $scale;
                        break;
                }

                // Get font family from first typography setting
                if ( ! empty( $typo['typography_font_family'] ) && 'inherit' === $typography['fontFamily'] ) {
                    $typography['fontFamily'] = sanitize_text_field( $typo['typography_font_family'] );
                }
            }
        }

        // H1-H6 typography from theme style
        $heading_tags = array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' );
        foreach ( $heading_tags as $tag ) {
            $key = $tag . '_typography_typography';
            if ( ! empty( $kit_settings[ $key ] ) ) {
                $typography['customScales'][] = array(
                    'name'       => strtoupper( $tag ),
                    'fontSize'   => $this->get_elementor_size( $kit_settings, $tag . '_typography_font_size' ),
                    'fontWeight' => sanitize_text_field( $kit_settings[ $tag . '_typography_font_weight' ] ?? '600' ),
                    'lineHeight' => $this->get_elementor_size( $kit_settings, $tag . '_typography_line_height' ),
                    'fontFamily' => sanitize_text_field( $kit_settings[ $tag . '_typography_font_family' ] ?? 'inherit' ),
                );
            }
        }

        // Body typography
        if ( ! empty( $kit_settings['body_typography_typography'] ) ) {
            $typography['bodyScale'] = array(
                'name'       => 'Body',
                'fontSize'   => $this->get_elementor_size( $kit_settings, 'body_typography_font_size' ),
                'fontWeight' => sanitize_text_field( $kit_settings['body_typography_font_weight'] ?? '400' ),
                'lineHeight' => $this->get_elementor_size( $kit_settings, 'body_typography_line_height' ),
                'fontFamily' => sanitize_text_field( $kit_settings['body_typography_font_family'] ?? 'inherit' ),
            );

            if ( ! empty( $kit_settings['body_typography_font_family'] ) ) {
                $typography['fontFamily'] = sanitize_text_field( $kit_settings['body_typography_font_family'] );
            }
        }

        return $typography;
    }

    /**
     * Extract full typography scale from Elementor typography settings.
     *
     * @param array $typo Elementor typography array.
     * @return array Full typography scale with all properties.
     */
    private function extract_full_typography_scale( $typo ) {
        return array(
            'fontFamily'     => sanitize_text_field( $typo['typography_font_family'] ?? 'inherit' ),
            'fontSize'       => $this->get_elementor_size( $typo, 'typography_font_size' ),
            'fontWeight'     => sanitize_text_field( $typo['typography_font_weight'] ?? '400' ),
            'textTransform'  => sanitize_text_field( $typo['typography_text_transform'] ?? 'none' ),
            'fontStyle'      => sanitize_text_field( $typo['typography_font_style'] ?? 'normal' ),
            'textDecoration' => sanitize_text_field( $typo['typography_text_decoration'] ?? 'none' ),
            'lineHeight'     => $this->get_elementor_size( $typo, 'typography_line_height' ),
            'letterSpacing'  => $this->get_elementor_size( $typo, 'typography_letter_spacing' ),
            'wordSpacing'    => $this->get_elementor_size( $typo, 'typography_word_spacing' ),
        );
    }

    /**
     * Get size value from Elementor settings.
     *
     * @param array  $settings Settings array.
     * @param string $key      Setting key.
     * @return string Size value with unit.
     */
    private function get_elementor_size( $settings, $key ) {
        if ( ! empty( $settings[ $key ]['size'] ) ) {
            $size = floatval( $settings[ $key ]['size'] );
            $unit = sanitize_text_field( $settings[ $key ]['unit'] ?? 'px' );
            return $size . $unit;
        }
        // Check if it's a direct value (like line height without unit)
        if ( isset( $settings[ $key ] ) && is_numeric( $settings[ $key ] ) ) {
            return (string) $settings[ $key ];
        }
        return '';
    }

    /**
     * Link a bot to this appearance preset.
     *
     * Sets appearance_preset_id on the bot.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|WP_Error
     */
    public function link_bot( $request ) {
        $preset_id = absint( $request->get_param( 'id' ) );
        $bot_id    = absint( $request->get_param( 'bot_id' ) );

        // Verify preset exists
        $model  = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $preset_id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        // Verify bot exists
        $bot_model = new BoostBot_Bot_Model();
        $bot       = $bot_model->get( $bot_id );

        if ( ! $bot ) {
            return $this->not_found( 'Bot' );
        }

        // Link the bot to this preset
        $result = $bot_model->update( $bot_id, array(
            'appearance_preset_id' => $preset_id,
        ) );

        if ( ! $result ) {
            return $this->error( __( 'Failed to link bot to appearance preset.', 'boostbot' ) );
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot linked to appearance preset', array(
                'bot_id'    => $bot_id,
                'preset_id' => $preset_id,
            ) );
        }

        return $this->success( array(
            'message' => __( 'Bot linked to appearance preset successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Unlink a bot from this appearance preset.
     *
     * Sets appearance_preset_id = NULL on the bot.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|WP_Error
     */
    public function unlink_bot( $request ) {
        $preset_id = absint( $request->get_param( 'id' ) );
        $bot_id    = absint( $request->get_param( 'bot_id' ) );

        // Verify preset exists
        $model  = new BoostBot_Appearance_Preset_Model();
        $preset = $model->get( $preset_id );

        if ( ! $preset ) {
            return $this->not_found( 'Appearance Preset' );
        }

        // Verify bot exists and is linked to this preset
        $bot_model = new BoostBot_Bot_Model();
        $bot       = $bot_model->get( $bot_id );

        if ( ! $bot || absint( $bot->appearance_preset_id ) !== $preset_id ) {
            return $this->not_found( 'Bot' );
        }

        // Unlink the bot
        $result = $bot_model->update( $bot_id, array(
            'appearance_preset_id' => null,
        ) );

        if ( ! $result ) {
            return $this->error( __( 'Failed to unlink bot from appearance preset.', 'boostbot' ) );
        }

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Bot unlinked from appearance preset', array(
                'bot_id'    => $bot_id,
                'preset_id' => $preset_id,
            ) );
        }

        return $this->success( array(
            'message' => __( 'Bot unlinked from appearance preset successfully.', 'boostbot' ),
        ) );
    }

    /**
     * Bulk delete presets.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|WP_Error
     */
    public function bulk_delete( WP_REST_Request $request ) {
        $data = $request->get_json_params();
        $ids = $data['ids'] ?? array();

        if ( empty( $ids ) || ! is_array( $ids ) ) {
            return $this->error( __( 'No items selected for deletion.', 'boostbot' ) );
        }

        $model = new BoostBot_Appearance_Preset_Model();
        $deleted_count = 0;
        $skipped_system = 0;
        $errors = array();

        foreach ( $ids as $id ) {
            $id = absint( $id );
            $preset = $model->get( $id );

            if ( ! $preset ) {
                continue;
            }

            // Skip system presets
            if ( ! empty( $preset->is_system ) ) {
                $skipped_system++;
                continue;
            }

            $result = $model->delete( $id );
            if ( $result ) {
                $deleted_count++;
            } else {
                $errors[] = $id;
            }
        }

        /* translators: %d: number of deleted presets */
        $message = sprintf( __( '%d appearance presets deleted successfully.', 'boostbot' ), $deleted_count );
        
        if ( $skipped_system > 0 ) {
            /* translators: %d: number of skipped system presets */
            $message .= sprintf( __( ' (%d system presets were not deleted)', 'boostbot' ), $skipped_system );
        }

        return $this->success( array(
            'deleted_count'  => $deleted_count,
            'skipped_system' => $skipped_system,
            'errors'         => $errors,
            'message'        => $message,
        ) );
    }

    /**
     * Get CSS for Elementor custom fonts.
     *
     * Checks if the font name matches an Elementor custom font and returns the @font-face CSS.
     *
     * @param string $font_family The font family name.
     * @return string|null The CSS for the custom font, or null if not found.
     */
    private function get_elementor_custom_font_css( $font_family ) {
        // Check if Elementor Pro is active
        if ( ! defined( 'ELEMENTOR_PRO_VERSION' ) ) {
            return null;
        }

        // Query for custom fonts
        $custom_fonts = get_posts( array(
            'post_type'      => 'elementor_font',
            'posts_per_page' => -1,
            'post_status'    => 'publish',
        ) );

        if ( empty( $custom_fonts ) ) {
            return null;
        }

        $css = '';

        foreach ( $custom_fonts as $font_post ) {
            $font_name = $font_post->post_title;
            
            // Check if this font matches the requested font family
            if ( strcasecmp( trim( $font_name ), trim( $font_family ) ) !== 0 ) {
                continue;
            }

            // Get font face data from Elementor
            $font_face = get_post_meta( $font_post->ID, 'elementor_font_face', true );
            
            if ( ! empty( $font_face ) ) {
                $css .= $font_face;
            } else {
                // Try to build font-face from font files
                $font_data = get_post_meta( $font_post->ID, 'elementor_font_files', true );
                
                if ( ! empty( $font_data ) && is_array( $font_data ) ) {
                    foreach ( $font_data as $weight => $files ) {
                        if ( ! is_array( $files ) ) {
                            continue;
                        }
                        
                        $sources = array();
                        
                        // Build src for each file type
                        foreach ( $files as $type => $file_data ) {
                            if ( ! empty( $file_data['url'] ) ) {
                                $url = esc_url( $file_data['url'] );
                                $format = '';
                                
                                switch ( $type ) {
                                    case 'woff2':
                                        $format = "format('woff2')";
                                        break;
                                    case 'woff':
                                        $format = "format('woff')";
                                        break;
                                    case 'ttf':
                                        $format = "format('truetype')";
                                        break;
                                    case 'svg':
                                        $format = "format('svg')";
                                        break;
                                    case 'eot':
                                        $format = "format('embedded-opentype')";
                                        break;
                                }
                                
                                if ( $format ) {
                                    $sources[] = "url('{$url}') {$format}";
                                }
                            }
                        }
                        
                        if ( ! empty( $sources ) ) {
                            $font_weight = is_numeric( $weight ) ? $weight : '400';
                            $src = implode( ', ', $sources );
                            
                            $css .= "@font-face {
    font-family: '{$font_name}';
    src: {$src};
    font-weight: {$font_weight};
    font-style: normal;
    font-display: swap;
}
";
                        }
                    }
                }
            }
        }

        return ! empty( $css ) ? $css : null;
    }

    /**
     * Duplicate an appearance preset.
     *
     * Copies all fields (including global_values, element_settings, widget_settings)
     * but resets is_default and is_system to 0.
     *
     * @param WP_REST_Request $request The request.
     * @return WP_REST_Response|WP_Error The response.
     */
    public function duplicate_preset( $request ) {
        $id = absint( $request->get_param( 'id' ) );

        $model  = new BoostBot_Appearance_Preset_Model();
        $original = $model->get( $id );

        if ( ! $original ) {
            return $this->not_found( 'Appearance Preset' );
        }

        // Convert object to array for duplication.
        $data = (array) $original;

        // Remove fields that should not be copied.
        unset( $data['id'], $data['created_at'], $data['updated_at'] );

        // Reset flags and generate new name/slug.
        $data['name']       = $original->name . __( ' (Copy)', 'boostbot' );
        $data['slug']       = $model->generate_slug( $data['name'] );
        $data['is_default'] = 0;
        $data['is_system']  = 0;

        // Create the duplicate.
        $new_id = $model->create( $data );

        if ( ! $new_id ) {
            return $this->error( __( 'Failed to duplicate appearance preset.', 'boostbot' ) );
        }

        $preset = $model->get( $new_id );

        if ( class_exists( 'BoostBot_Logger' ) ) {
            BoostBot_Logger::info( 'Appearance preset duplicated', array(
                'original_id' => $id,
                'new_id'      => $new_id,
            ) );
        }

        return $this->success( array(
            'preset'  => $preset,
            'message' => __( 'Appearance preset duplicated successfully.', 'boostbot' ),
        ), 201 );
    }
}

