<?php
/**
 * GGM Commerce AJAX Class
 * Handles all AJAX requests and product filtering functionality
 */

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

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter

class GGM_Commerce_Ajax {
    
    public function __construct() {
        // Register AJAX actions
        add_action('wp_ajax_ggm_commerce_filter_products', array($this, 'filter_products'));
        add_action('wp_ajax_nopriv_ggm_commerce_filter_products', array($this, 'filter_products'));

        // Address management AJAX actions (logged-in users only)
        add_action('wp_ajax_ggm_commerce_get_address', array($this, 'get_address'));
        add_action('wp_ajax_ggm_commerce_save_address', array($this, 'save_address'));
        add_action('wp_ajax_ggm_commerce_delete_address', array($this, 'delete_address'));
        add_action('wp_ajax_ggm_commerce_set_default_address', array($this, 'set_default_address'));

        // Business settings AJAX actions (logged-in users only)
        add_action('wp_ajax_ggm_commerce_update_business', array($this, 'update_business'));
        add_action('wp_ajax_ggm_commerce_update_price_preference', array($this, 'update_price_preference'));

        // Tax rate management AJAX actions (admin only)
        add_action('wp_ajax_ggm_commerce_add_tax_rate', array($this, 'add_tax_rate'));
        add_action('wp_ajax_ggm_commerce_update_tax_rate', array($this, 'update_tax_rate'));
        add_action('wp_ajax_ggm_commerce_delete_tax_rate', array($this, 'delete_tax_rate'));

        // Variation gallery images AJAX (for both logged in and guests)
        add_action('wp_ajax_ggmc_get_variation_gallery_images', array($this, 'get_variation_gallery_images'));
        add_action('wp_ajax_nopriv_ggmc_get_variation_gallery_images', array($this, 'get_variation_gallery_images'));

        // Login and registration AJAX (for non-logged-in users)
        add_action('wp_ajax_nopriv_ggm_commerce_login', array($this, 'ajax_login'));
        add_action('wp_ajax_nopriv_ggm_commerce_register', array($this, 'ajax_register'));
    }
    
    /**
     * AJAX handler for product filtering
     */
    public function filter_products() {
        // Verify nonce
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce is verified, not used as data
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ggm_commerce_nonce')) {
            wp_die('Security check failed');
        }
        
        global $wpdb;

        // Parse filter data safely into an array (parse_str with second param is safe)
        // Note: sanitize_text_field must NOT be applied before parse_str because it strips
        // [] brackets needed for checkbox array parameters (e.g. attr_5[]=red&attr_5[]=blue).
        // Individual values are sanitized after parsing via sanitize_text_field in the filter handlers below.
        $filters = [];
        if (!empty($_POST['filter_data'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Individual values are sanitized after parse_str; applying sanitize_text_field before parse_str destroys array bracket syntax
            parse_str(wp_unslash($_POST['filter_data']), $filters);
        }

        // Build query with proper JOINs for relationship tables
        $table_name = $wpdb->prefix . 'ggmc_products';
        $where_conditions = array('(p.status = "active" OR p.status IS NULL)');
        $joins = array();

        // Scope to products present in the page DOM (e.g. category pages
        // only render a subset of products). This ensures filter results
        // and attribute counts reflect only the products actually shown.
        if ( ! empty( $_POST['scope_product_ids'] ) ) {
            $scope_ids = array_filter( array_map( 'intval', explode( ',', sanitize_text_field( wp_unslash( $_POST['scope_product_ids'] ) ) ) ) );
            if ( ! empty( $scope_ids ) ) {
                $scope_placeholder = implode( ',', $scope_ids );
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $scope_placeholder is sanitized via intval
                $where_conditions[] = "p.id IN ({$scope_placeholder})";
            }
        }

        // Search term filter
        if (!empty($filters['search'])) {
            $search_term = sanitize_text_field($filters['search'] ?? '');
            $where_conditions[] = $wpdb->prepare(
                "(p.name LIKE %s OR p.description LIKE %s OR p.short_description LIKE %s)",
                '%' . $wpdb->esc_like($search_term) . '%',
                '%' . $wpdb->esc_like($search_term) . '%',
                '%' . $wpdb->esc_like($search_term) . '%'
            );
        }

        // Category filter - use product_categories relationship table
        if (!empty($filters['category'])) {
            $category_id = intval($filters['category']);
            $joins[] = "INNER JOIN {$wpdb->prefix}ggmc_product_categories pc ON p.id = pc.product_id";
            $where_conditions[] = $wpdb->prepare("pc.category_id = %d", $category_id);
        }

        // Tags filter - use product_tags relationship table
        if (!empty($filters['tags'])) {
            $tag_id = intval($filters['tags']);
            $joins[] = "INNER JOIN {$wpdb->prefix}ggmc_product_tags pt ON p.id = pt.product_id";
            $where_conditions[] = $wpdb->prepare("pt.tag_id = %d", $tag_id);
        }

        // Price range filter - only apply if user actually changed from defaults
        // Get actual price range to determine defaults
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $table_name is a safe table prefix construction
        $actual_price_range = $wpdb->get_row("
            SELECT
                FLOOR(MIN(price)) as min_price,
                CEIL(MAX(price)) as max_price
            FROM {$table_name}
            WHERE (status = 'active' OR status IS NULL)
        ");

        $actual_min = $actual_price_range ? floatval($actual_price_range->min_price) : 0;
        $actual_max = $actual_price_range ? floatval($actual_price_range->max_price) : 1000;

        // Only apply min price filter if user moved slider above the minimum
        // Use a small epsilon for floating point comparison
        if (!empty($filters['min_price']) && (floatval($filters['min_price']) - $actual_min) > 0.01) {
            $min_price = floatval($filters['min_price']);
            $where_conditions[] = $wpdb->prepare("p.price >= %f", $min_price);
        }

        // Only apply max price filter if user moved slider below the maximum
        // Use a small epsilon for floating point comparison
        if (!empty($filters['max_price']) && ($actual_max - floatval($filters['max_price'])) > 0.01) {
            $max_price = floatval($filters['max_price']);
            $where_conditions[] = $wpdb->prepare("p.price <= %f", $max_price);
        }

        // Stock status filter
        if (!empty($filters['stock'])) {
            $stock_status = sanitize_text_field($filters['stock'] ?? '');
            if ($stock_status === 'in_stock') {
                $where_conditions[] = "p.stock > 0 AND p.stock_status = 'instock'";
            } elseif ($stock_status === 'out_of_stock') {
                $where_conditions[] = "(p.stock = 0 OR p.stock_status = 'outofstock')";
            }
        }

        // Attribute filters — each attribute uses a subquery so that
        // multiple attributes are combined with AND (every filter narrows).
        $attr_filters = $this->extract_attribute_filters($filters);
        if (!empty($attr_filters)) {
            $attr_subqueries = $this->build_attribute_filter_subqueries($attr_filters);
            foreach ($attr_subqueries as $subquery) {
                $where_conditions[] = $subquery;
            }
        }

        // Build the complete query
        $join_sql = !empty($joins) ? implode(' ', $joins) : '';
        $where_sql = 'WHERE ' . implode(' AND ', $where_conditions);

        $query = "
            SELECT DISTINCT p.*
            FROM {$table_name} p
            {$join_sql}
            {$where_sql}
            ORDER BY p.name ASC
        ";

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared -- $query is built from sanitized inputs and safe table names
        $products = $wpdb->get_results($query);

        // Get product IDs
        $product_ids = array_map(function($product) {
            return intval($product->id);
        }, $products);

        // Calculate category and tag counts for filtered results
        $category_counts = [];
        $tag_counts = [];

        // Get category counts from relationship table
        if (!empty($product_ids)) {
            $ids_placeholder = implode(',', array_map('intval', $product_ids));
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $ids_placeholder is sanitized via intval
            $cat_count_results = $wpdb->get_results(
                "SELECT category_id, COUNT(*) as count
                 FROM {$wpdb->prefix}ggmc_product_categories
                 WHERE product_id IN ($ids_placeholder)
                 GROUP BY category_id"
            );
            foreach ($cat_count_results as $row) {
                $category_counts[intval($row->category_id)] = intval($row->count);
            }
        }

        // Get tag counts from relationship table
        if (!empty($product_ids)) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $ids_placeholder is sanitized via intval
            $tag_count_results = $wpdb->get_results(
                "SELECT tag_id, COUNT(*) as count
                 FROM {$wpdb->prefix}ggmc_product_tags
                 WHERE product_id IN ($ids_placeholder)
                 GROUP BY tag_id"
            );
            foreach ($tag_count_results as $row) {
                $tag_counts[intval($row->tag_id)] = intval($row->count);
            }
        }

        // Get attribute counts for filterable attributes (select type only)
        $attribute_counts = $this->get_attribute_counts_for_products($product_ids);

        // Calculate the price range of filtered products (excluding the
        // price filter itself) so the slider can update its bounds.
        $filtered_price_range = array( 'min' => $actual_min, 'max' => $actual_max );
        if ( ! empty( $product_ids ) ) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $ids_placeholder is sanitized via intval
            $price_range_result = $wpdb->get_row(
                "SELECT FLOOR(MIN(price)) as min_price, CEIL(MAX(price)) as max_price
                 FROM {$table_name}
                 WHERE id IN ({$ids_placeholder}) AND (status = 'active' OR status IS NULL)"
            );
            if ( $price_range_result ) {
                $filtered_price_range['min'] = floatval( $price_range_result->min_price );
                $filtered_price_range['max'] = floatval( $price_range_result->max_price );
            }
        }

        wp_send_json_success(array(
            'product_ids' => $product_ids,
            'count' => count($products),
            'total_found' => count($products),
            'category_counts' => $category_counts,
            'tag_counts' => $tag_counts,
            'attribute_counts' => $attribute_counts,
            'price_range' => $filtered_price_range,
            'message' => count($products) > 0 ?
                /* translators: %d: number of products found */
                sprintf(__('Found %d products', 'ggm-commerce'), count($products)) :
                __('No products found matching your criteria.', 'ggm-commerce')
        ));
    }

    /**
     * Extract attribute filter parameters from filter data.
     *
     * @param array $filters Filter data array.
     * @return array Attribute filters keyed by type (select, number_range, text).
     */
    private function extract_attribute_filters($filters) {
        $attr_filters = array(
            'select' => array(),       // attr_ID => [values]
            'number_range' => array(), // attr_ID => ['min' => x, 'max' => y]
            'text' => array(),         // attr_ID => search_term
        );

        foreach ($filters as $key => $value) {
            if (empty($value)) {
                continue;
            }

            // Match attr_ID or attr_ID[] (checkbox array)
            if (preg_match('/^attr_(\d+)$/', $key, $matches)) {
                $attr_id = intval($matches[1]);
                if (is_array($value)) {
                    // Checkbox array - select type
                    $attr_filters['select'][$attr_id] = array_map('sanitize_text_field', $value);
                } else {
                    // Could be dropdown select or text search
                    $attr_filters['text'][$attr_id] = sanitize_text_field($value);
                }
            }
            // Match attr_ID_min (number range)
            elseif (preg_match('/^attr_(\d+)_min$/', $key, $matches)) {
                $attr_id = intval($matches[1]);
                if (!isset($attr_filters['number_range'][$attr_id])) {
                    $attr_filters['number_range'][$attr_id] = array();
                }
                $attr_filters['number_range'][$attr_id]['min'] = floatval($value);
            }
            // Match attr_ID_max (number range)
            elseif (preg_match('/^attr_(\d+)_max$/', $key, $matches)) {
                $attr_id = intval($matches[1]);
                if (!isset($attr_filters['number_range'][$attr_id])) {
                    $attr_filters['number_range'][$attr_id] = array();
                }
                $attr_filters['number_range'][$attr_id]['max'] = floatval($value);
            }
        }

        return $attr_filters;
    }

    /**
     * Build SQL WHERE conditions for attribute filters.
     *
     * @param array $attr_filters Attribute filters from extract_attribute_filters().
     * @return string SQL condition string or empty.
     */
    private function build_attribute_filter_conditions($attr_filters) {
        global $wpdb;

        $conditions = array();

        // Process select/dropdown filters
        foreach ($attr_filters['select'] as $attr_id => $values) {
            if (empty($values)) {
                continue;
            }
            $placeholders = implode(',', array_fill(0, count($values), '%s'));
            $params = array_merge(array($attr_id), $values);
            $conditions[] = $wpdb->prepare(
                "(pav.attribute_id = %d AND pav.attribute_value IN ($placeholders))",
                $params
            );
        }

        // Process text filters (could be dropdown single value or text search)
        foreach ($attr_filters['text'] as $attr_id => $value) {
            if (empty($value)) {
                continue;
            }
            // Get attribute type to determine if this is exact match or LIKE
            $attr = $this->get_attribute_type($attr_id);
            if ($attr && $attr->attribute_type === 'text') {
                // Text type - use LIKE for partial match
                $conditions[] = $wpdb->prepare(
                    "(pav.attribute_id = %d AND pav.attribute_value LIKE %s)",
                    $attr_id,
                    '%' . $wpdb->esc_like($value) . '%'
                );
            } else {
                // Select dropdown - exact match
                $conditions[] = $wpdb->prepare(
                    "(pav.attribute_id = %d AND pav.attribute_value = %s)",
                    $attr_id,
                    $value
                );
            }
        }

        // Process number range filters
        foreach ($attr_filters['number_range'] as $attr_id => $range) {
            $range_parts = array();
            $range_parts[] = $wpdb->prepare("pav.attribute_id = %d", $attr_id);

            // Get the actual value range to check if user changed from defaults
            $attr_manager = class_exists('GGM_Commerce_Attributes') ? GGM_Commerce_Attributes::get_instance() : null;
            $actual_range = $attr_manager ? $attr_manager->get_attribute_value_range($attr_id) : array('min' => 0, 'max' => 100);

            // Only apply min if different from actual min
            if (isset($range['min']) && abs($range['min'] - $actual_range['min']) > 0.01) {
                $range_parts[] = $wpdb->prepare(
                    "CAST(pav.attribute_value AS DECIMAL(10,2)) >= %f",
                    $range['min']
                );
            }

            // Only apply max if different from actual max
            if (isset($range['max']) && abs($actual_range['max'] - $range['max']) > 0.01) {
                $range_parts[] = $wpdb->prepare(
                    "CAST(pav.attribute_value AS DECIMAL(10,2)) <= %f",
                    $range['max']
                );
            }

            // Only add condition if we have more than just the attribute_id check
            if (count($range_parts) > 1) {
                $conditions[] = '(' . implode(' AND ', $range_parts) . ')';
            }
        }

        if (empty($conditions)) {
            return '';
        }

        // Use OR between different attribute conditions (match any)
        return '(' . implode(' OR ', $conditions) . ')';
    }

    /**
     * Build per-attribute EXISTS subqueries for AND logic.
     *
     * Each attribute filter becomes a separate EXISTS subquery so that
     * multiple attributes are combined with AND — every filter narrows
     * the results rather than expanding them.
     *
     * @param array $attr_filters Attribute filters from extract_attribute_filters().
     * @return array Array of SQL subquery strings.
     */
    private function build_attribute_filter_subqueries( $attr_filters ) {
        global $wpdb;

        $subqueries = array();
        $pav_table  = $wpdb->prefix . 'ggmc_product_attribute_values';

        // Process select/dropdown filters (checkbox arrays)
        foreach ( $attr_filters['select'] as $attr_id => $values ) {
            if ( empty( $values ) ) {
                continue;
            }
            $placeholders = implode( ',', array_fill( 0, count( $values ), '%s' ) );
            $params       = array_merge( array( $attr_id ), $values );
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare -- $pav_table is safe, $placeholders is generated
            $subqueries[] = $wpdb->prepare(
                "EXISTS (SELECT 1 FROM {$pav_table} sub_pav WHERE sub_pav.product_id = p.id AND sub_pav.attribute_id = %d AND sub_pav.attribute_value IN ({$placeholders}))",
                $params
            );
        }

        // Process text filters (dropdown single value or text search)
        foreach ( $attr_filters['text'] as $attr_id => $value ) {
            if ( empty( $value ) ) {
                continue;
            }
            $attr = $this->get_attribute_type( $attr_id );
            if ( $attr && $attr->attribute_type === 'text' ) {
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pav_table is safe
                $subqueries[] = $wpdb->prepare(
                    "EXISTS (SELECT 1 FROM {$pav_table} sub_pav WHERE sub_pav.product_id = p.id AND sub_pav.attribute_id = %d AND sub_pav.attribute_value LIKE %s)",
                    $attr_id,
                    '%' . $wpdb->esc_like( $value ) . '%'
                );
            } else {
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pav_table is safe
                $subqueries[] = $wpdb->prepare(
                    "EXISTS (SELECT 1 FROM {$pav_table} sub_pav WHERE sub_pav.product_id = p.id AND sub_pav.attribute_id = %d AND sub_pav.attribute_value = %s)",
                    $attr_id,
                    $value
                );
            }
        }

        // Process number range filters
        foreach ( $attr_filters['number_range'] as $attr_id => $range ) {
            $range_parts   = array();
            $range_parts[] = $wpdb->prepare( 'sub_pav.attribute_id = %d', $attr_id );

            $attr_manager = class_exists( 'GGM_Commerce_Attributes' ) ? GGM_Commerce_Attributes::get_instance() : null;
            $actual_range = $attr_manager ? $attr_manager->get_attribute_value_range( $attr_id ) : array( 'min' => 0, 'max' => 100 );

            if ( isset( $range['min'] ) && abs( $range['min'] - $actual_range['min'] ) > 0.01 ) {
                $range_parts[] = $wpdb->prepare( 'CAST(sub_pav.attribute_value AS DECIMAL(10,2)) >= %f', $range['min'] );
            }
            if ( isset( $range['max'] ) && abs( $actual_range['max'] - $range['max'] ) > 0.01 ) {
                $range_parts[] = $wpdb->prepare( 'CAST(sub_pav.attribute_value AS DECIMAL(10,2)) <= %f', $range['max'] );
            }

            if ( count( $range_parts ) > 1 ) {
                // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $pav_table is safe
                $subqueries[] = "EXISTS (SELECT 1 FROM {$pav_table} sub_pav WHERE sub_pav.product_id = p.id AND " . implode( ' AND ', $range_parts ) . ')';
            }
        }

        return $subqueries;
    }

    /**
     * Get attribute type by ID.
     *
     * @param int $attr_id Attribute ID.
     * @return object|null Attribute object or null.
     */
    private function get_attribute_type($attr_id) {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        return $wpdb->get_row(
            $wpdb->prepare(
                "SELECT attribute_type FROM {$wpdb->prefix}ggmc_attributes WHERE id = %d",
                $attr_id
            )
        );
    }

    /**
     * Get attribute option counts for filtered products.
     *
     * @param array $product_ids Array of product IDs.
     * @return array Attribute counts keyed by attribute ID then value.
     */
    private function get_attribute_counts_for_products($product_ids) {
        global $wpdb;

        if (empty($product_ids)) {
            return array();
        }

        // Get filterable attributes with select type
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $filterable_attrs = $wpdb->get_results(
            "SELECT id FROM {$wpdb->prefix}ggmc_attributes
             WHERE filterable = 1 AND status = 'active' AND attribute_type = 'select'"
        );

        if (empty($filterable_attrs)) {
            return array();
        }

        $attribute_counts = array();
        $ids_placeholder = implode(',', array_map('intval', $product_ids));
        $attr_ids = array_map(function($a) { return intval($a->id); }, $filterable_attrs);
        $attr_ids_placeholder = implode(',', $attr_ids);

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Placeholders are sanitized via intval
        $counts = $wpdb->get_results(
            "SELECT pav.attribute_id, pav.attribute_value, COUNT(DISTINCT pav.product_id) as count
             FROM {$wpdb->prefix}ggmc_product_attribute_values pav
             WHERE pav.attribute_id IN ($attr_ids_placeholder)
             AND pav.product_id IN ($ids_placeholder)
             AND pav.attribute_value != ''
             GROUP BY pav.attribute_id, pav.attribute_value"
        );

        foreach ($counts as $row) {
            $attr_id = intval($row->attribute_id);
            if (!isset($attribute_counts[$attr_id])) {
                $attribute_counts[$attr_id] = array();
            }
            $attribute_counts[$attr_id][$row->attribute_value] = intval($row->count);
        }

        return $attribute_counts;
    }

    /**
     * AJAX handler to get address details
     */
    public function get_address() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'You must be logged in to perform this action.']);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_address_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => 'Security check failed.']);
            return;
        }

        global $ggm_commerce_address_manager;
        $user_id = get_current_user_id();
        $address_id = intval($_POST['address_id'] ?? 0);

        if (!$address_id) {
            wp_send_json_error(['message' => 'Invalid address ID.']);
            return;
        }

        $address = $ggm_commerce_address_manager->get_address($address_id, $user_id);

        if (!$address) {
            wp_send_json_error(['message' => 'Address not found or access denied.']);
            return;
        }

        wp_send_json_success(['address' => $address]);
    }

    /**
     * AJAX handler to save (add or update) address
     */
    public function save_address() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'You must be logged in to perform this action.']);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_address_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => 'Security check failed.']);
            return;
        }

        global $ggm_commerce_address_manager;
        $user_id = get_current_user_id();
        $address_id = intval($_POST['address_id'] ?? 0);

        // Prepare address data
        $address_data = [
            'address_type' => isset($_POST['address_type']) ? sanitize_text_field(wp_unslash($_POST['address_type'])) : 'billing',
            'address_label' => isset($_POST['address_label']) ? sanitize_text_field(wp_unslash($_POST['address_label'])) : '',
            'first_name' => isset($_POST['first_name']) ? sanitize_text_field(wp_unslash($_POST['first_name'])) : '',
            'last_name' => isset($_POST['last_name']) ? sanitize_text_field(wp_unslash($_POST['last_name'])) : '',
            'company' => isset($_POST['company']) ? sanitize_text_field(wp_unslash($_POST['company'])) : '',
            'address_1' => isset($_POST['address_1']) ? sanitize_text_field(wp_unslash($_POST['address_1'])) : '',
            'address_2' => isset($_POST['address_2']) ? sanitize_text_field(wp_unslash($_POST['address_2'])) : '',
            'city' => isset($_POST['city']) ? sanitize_text_field(wp_unslash($_POST['city'])) : '',
            'state' => isset($_POST['state']) ? sanitize_text_field(wp_unslash($_POST['state'])) : '',
            'postcode' => isset($_POST['postcode']) ? sanitize_text_field(wp_unslash($_POST['postcode'])) : '',
            'country' => isset($_POST['country']) ? sanitize_text_field(wp_unslash($_POST['country'])) : '',
            'phone' => isset($_POST['phone']) ? sanitize_text_field(wp_unslash($_POST['phone'])) : '',
            'is_default' => isset($_POST['is_default']) && $_POST['is_default'] === 'true' ? true : false
        ];

        // Update or add
        if ($address_id) {
            $result = $ggm_commerce_address_manager->update_address($address_id, $user_id, $address_data);
        } else {
            $result = $ggm_commerce_address_manager->add_address($user_id, $address_data);
        }

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            wp_send_json_success([
                'message' => $address_id ? 'Address updated successfully.' : 'Address added successfully.',
                'address_id' => $result['address_id'] ?? $address_id
            ]);
        }
    }

    /**
     * AJAX handler to delete address
     */
    public function delete_address() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'You must be logged in to perform this action.']);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_address_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => 'Security check failed.']);
            return;
        }

        global $ggm_commerce_address_manager;
        $user_id = get_current_user_id();
        $address_id = intval($_POST['address_id'] ?? 0);

        if (!$address_id) {
            wp_send_json_error(['message' => 'Invalid address ID.']);
            return;
        }

        $result = $ggm_commerce_address_manager->delete_address($address_id, $user_id);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            wp_send_json_success(['message' => 'Address deleted successfully.']);
        }
    }

    /**
     * AJAX handler to set default address
     */
    public function set_default_address() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'You must be logged in to perform this action.']);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_address_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => 'Security check failed.']);
            return;
        }

        global $ggm_commerce_address_manager;
        $user_id = get_current_user_id();
        $address_id = intval($_POST['address_id'] ?? 0);

        if (!$address_id) {
            wp_send_json_error(['message' => 'Invalid address ID.']);
            return;
        }

        $result = $ggm_commerce_address_manager->set_default_address($address_id, $user_id);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            wp_send_json_success(['message' => 'Default address updated successfully.']);
        }
    }

    /**
     * AJAX handler to update business details (company name, tax ID)
     */
    public function update_business() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => __('You must be logged in to perform this action.', 'ggm-commerce')]);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_business_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        // Check if B2B is enabled
        $tax = ggm_commerce_tax();
        if (!$tax->is_b2b_enabled()) {
            wp_send_json_error(['message' => __('B2B tax exemption is not enabled.', 'ggm-commerce')]);
            return;
        }

        $user_id = get_current_user_id();
        $company_name = isset($_POST['company_name']) ? sanitize_text_field(wp_unslash($_POST['company_name'])) : '';
        $tax_id = isset($_POST['tax_id']) ? sanitize_text_field(wp_unslash($_POST['tax_id'])) : '';

        if (empty($tax_id)) {
            wp_send_json_error(['message' => __('Tax ID is required.', 'ggm-commerce')]);
            return;
        }

        // Use the data handler to update business details
        require_once plugin_dir_path(__DIR__) . 'widgets/data/class-customer-account-data-handler.php';
        $data_handler = new GGM_Commerce_Customer_Account_Data_Handler();

        $result = $data_handler->update_customer_business($user_id, [
            'company_name' => $company_name,
            'tax_id' => $tax_id
        ]);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            $status = $result['tax_exempt_status'] ?? 'none';
            $message = __('Business details saved successfully.', 'ggm-commerce');
            if ($status === 'pending') {
                $message = __('Business details saved. Your tax exemption request is pending approval.', 'ggm-commerce');
            } elseif ($status === 'approved') {
                $message = __('Business details saved. Tax exemption is now active on your account.', 'ggm-commerce');
            }
            wp_send_json_success(['message' => $message, 'status' => $status]);
        }
    }

    /**
     * AJAX handler to update price display preference
     */
    public function update_price_preference() {
        // Verify user is logged in
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => __('You must be logged in to perform this action.', 'ggm-commerce')]);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_price_preference_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $user_id = get_current_user_id();
        $preference = isset($_POST['price_display']) ? sanitize_text_field(wp_unslash($_POST['price_display'])) : 'default';

        // Validate preference value
        $valid_preferences = ['default', 'excluding', 'including'];
        if (!in_array($preference, $valid_preferences, true)) {
            $preference = 'default';
        }

        // Use the data handler to update preference
        require_once plugin_dir_path(__DIR__) . 'widgets/data/class-customer-account-data-handler.php';
        $data_handler = new GGM_Commerce_Customer_Account_Data_Handler();

        $result = $data_handler->update_price_display_preference($user_id, $preference);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            wp_send_json_success(['message' => __('Price display preference saved successfully.', 'ggm-commerce')]);
        }
    }

    /**
     * AJAX handler to add a new tax rate
     */
    public function add_tax_rate() {
        // Verify admin permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'ggm-commerce')]);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_tax_rate_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
        $rate = isset($_POST['rate']) ? floatval($_POST['rate']) : 0;
        $description = isset($_POST['description']) ? sanitize_text_field(wp_unslash($_POST['description'])) : '';

        if (empty($name)) {
            wp_send_json_error(['message' => __('Tax rate name is required.', 'ggm-commerce')]);
            return;
        }

        $tax = ggm_commerce_tax();
        $result = $tax->create_tax_rate([
            'name' => $name,
            'rate' => $rate,
            'description' => $description,
        ]);

        if ($result) {
            wp_send_json_success(['message' => __('Tax rate added successfully.', 'ggm-commerce'), 'rate_id' => $result]);
        } else {
            wp_send_json_error(['message' => __('Failed to add tax rate. The slug may already exist.', 'ggm-commerce')]);
        }
    }

    /**
     * AJAX handler to update an existing tax rate
     */
    public function update_tax_rate() {
        // Verify admin permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'ggm-commerce')]);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_tax_rate_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $rate_id = isset($_POST['rate_id']) ? intval($_POST['rate_id']) : 0;
        $name = isset($_POST['name']) ? sanitize_text_field(wp_unslash($_POST['name'])) : '';
        $rate = isset($_POST['rate']) ? floatval($_POST['rate']) : 0;
        $is_default = isset($_POST['is_default']) ? intval($_POST['is_default']) : 0;

        if (!$rate_id) {
            wp_send_json_error(['message' => __('Invalid tax rate ID.', 'ggm-commerce')]);
            return;
        }

        if (empty($name)) {
            wp_send_json_error(['message' => __('Tax rate name is required.', 'ggm-commerce')]);
            return;
        }

        $tax = ggm_commerce_tax();

        // If setting as default, clear existing default first
        if ($is_default) {
            $tax->set_default_tax_rate($rate_id);
        }

        $result = $tax->update_tax_rate($rate_id, [
            'name' => $name,
            'rate' => $rate,
        ]);

        if ($result !== false) {
            wp_send_json_success(['message' => __('Tax rate updated successfully.', 'ggm-commerce')]);
        } else {
            wp_send_json_error(['message' => __('Failed to update tax rate.', 'ggm-commerce')]);
        }
    }

    /**
     * AJAX handler to delete a tax rate
     */
    public function delete_tax_rate() {
        // Verify admin permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('You do not have permission to perform this action.', 'ggm-commerce')]);
            return;
        }

        // Verify nonce
        if (!check_ajax_referer('ggm_commerce_tax_rate_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $rate_id = isset($_POST['rate_id']) ? intval($_POST['rate_id']) : 0;

        if (!$rate_id) {
            wp_send_json_error(['message' => __('Invalid tax rate ID.', 'ggm-commerce')]);
            return;
        }

        $tax = ggm_commerce_tax();

        // Check if this is the default rate
        $rate = $tax->get_tax_rate($rate_id);
        if ($rate && $rate->is_default) {
            wp_send_json_error(['message' => __('Cannot delete the default tax rate. Set another rate as default first.', 'ggm-commerce')]);
            return;
        }

        $result = $tax->delete_tax_rate($rate_id);

        if ($result) {
            wp_send_json_success(['message' => __('Tax rate deleted successfully.', 'ggm-commerce')]);
        } else {
            wp_send_json_error(['message' => __('Failed to delete tax rate.', 'ggm-commerce')]);
        }
    }

    /**
     * AJAX handler for getting variation gallery images
     *
     * Returns full image data for a variation's gallery images.
     */
    public function get_variation_gallery_images() {
        // Verify nonce
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce is verified, not used as data
        if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'ggm_commerce_nonce')) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $variation_id = isset($_POST['variation_id']) ? intval($_POST['variation_id']) : 0;

        if (!$variation_id) {
            wp_send_json_error(['message' => __('Invalid variation ID.', 'ggm-commerce')]);
            return;
        }

        // Check if variations class exists
        if (!class_exists('GGM_Commerce_Variations')) {
            wp_send_json_error(['message' => __('Variations feature not available.', 'ggm-commerce')]);
            return;
        }

        // Get variation
        $variations_manager = GGM_Commerce_Variations::instance();
        $variation = $variations_manager->get_variation($variation_id);

        if (!$variation) {
            wp_send_json_error(['message' => __('Variation not found.', 'ggm-commerce')]);
            return;
        }

        // Get image IDs from variation
        $image_ids = array();
        if (!empty($variation['image_id'])) {
            $image_ids[] = intval($variation['image_id']);
        }
        if (!empty($variation['gallery_ids']) && is_array($variation['gallery_ids'])) {
            foreach ($variation['gallery_ids'] as $gallery_id) {
                if (!in_array(intval($gallery_id), $image_ids, true)) {
                    $image_ids[] = intval($gallery_id);
                }
            }
        }

        if (empty($image_ids)) {
            wp_send_json_error(['message' => __('No images found for this variation.', 'ggm-commerce')]);
            return;
        }

        // Build image data array
        $images = array();
        foreach ($image_ids as $index => $image_id) {
            $attachment_post = get_post($image_id);
            if (!$attachment_post || $attachment_post->post_type !== 'attachment') {
                continue;
            }

            $images[] = array(
                'id'              => $image_id,
                'index'           => $index,
                'url'             => wp_get_attachment_url($image_id),
                'title'           => $attachment_post->post_title,
                'alt'             => get_post_meta($image_id, '_wp_attachment_image_alt', true),
                'caption'         => $attachment_post->post_excerpt,
                'thumbnail_url'   => wp_get_attachment_image_url($image_id, 'thumbnail'),
                'medium_url'      => wp_get_attachment_image_url($image_id, 'medium'),
                'large_url'       => wp_get_attachment_image_url($image_id, 'large'),
                'full_url'        => wp_get_attachment_image_url($image_id, 'full'),
                'thumbnail_srcset' => wp_get_attachment_image_srcset($image_id, 'thumbnail'),
                'medium_srcset'    => wp_get_attachment_image_srcset($image_id, 'medium'),
                'large_srcset'     => wp_get_attachment_image_srcset($image_id, 'large'),
                'full_srcset'      => wp_get_attachment_image_srcset($image_id, 'full'),
            );
        }

        if (empty($images)) {
            wp_send_json_error(['message' => __('Could not load image data.', 'ggm-commerce')]);
            return;
        }

        wp_send_json_success(array('images' => $images));
    }

    /**
     * AJAX handler for user login
     */
    public function ajax_login() {
        if (!check_ajax_referer('ggm_commerce_auth_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $username = isset($_POST['username']) ? sanitize_text_field(wp_unslash($_POST['username'])) : '';
        $password = isset($_POST['password']) ? $_POST['password'] : '';
        $remember = isset($_POST['remember']) && $_POST['remember'] === 'true';

        if (empty($username) || empty($password)) {
            wp_send_json_error(['message' => __('Please enter both username and password.', 'ggm-commerce')]);
            return;
        }

        require_once plugin_dir_path(__DIR__) . 'widgets/data/class-customer-account-data-handler.php';
        $data_handler = new GGM_Commerce_Customer_Account_Data_Handler();

        $result = $data_handler->login_user($username, $password, $remember);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            wp_send_json_success(['message' => __('Login successful.', 'ggm-commerce')]);
        }
    }

    /**
     * AJAX handler for user registration
     */
    public function ajax_register() {
        if (!check_ajax_referer('ggm_commerce_auth_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'ggm-commerce')]);
            return;
        }

        $username = isset($_POST['username']) ? sanitize_text_field(wp_unslash($_POST['username'])) : '';
        $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : '';
        $password = isset($_POST['password']) ? $_POST['password'] : '';
        $confirm_password = isset($_POST['confirm_password']) ? $_POST['confirm_password'] : '';
        $first_name = isset($_POST['first_name']) ? sanitize_text_field(wp_unslash($_POST['first_name'])) : '';
        $last_name = isset($_POST['last_name']) ? sanitize_text_field(wp_unslash($_POST['last_name'])) : '';

        if (empty($username) || empty($email) || empty($password)) {
            wp_send_json_error(['message' => __('Please fill in all required fields.', 'ggm-commerce')]);
            return;
        }

        if ($password !== $confirm_password) {
            wp_send_json_error(['message' => __('Passwords do not match.', 'ggm-commerce')]);
            return;
        }

        if (strlen($password) < 8) {
            wp_send_json_error(['message' => __('Password must be at least 8 characters long.', 'ggm-commerce')]);
            return;
        }

        $additional_data = [
            'first_name' => $first_name,
            'last_name' => $last_name,
        ];

        // Include business fields if present
        if (isset($_POST['account_type'])) {
            $additional_data['account_type'] = sanitize_text_field(wp_unslash($_POST['account_type']));
        }
        if (isset($_POST['company_name'])) {
            $additional_data['company_name'] = sanitize_text_field(wp_unslash($_POST['company_name']));
        }
        if (isset($_POST['tax_id'])) {
            $additional_data['tax_id'] = sanitize_text_field(wp_unslash($_POST['tax_id']));
        }

        require_once plugin_dir_path(__DIR__) . 'widgets/data/class-customer-account-data-handler.php';
        $data_handler = new GGM_Commerce_Customer_Account_Data_Handler();

        $result = $data_handler->register_user($username, $email, $password, $additional_data);

        if (isset($result['error'])) {
            wp_send_json_error(['message' => $result['error']]);
        } else {
            // Auto-login the user after registration
            wp_set_current_user($result['user_id']);
            wp_set_auth_cookie($result['user_id'], true);
            wp_send_json_success(['message' => __('Registration successful.', 'ggm-commerce')]);
        }
    }
}

// Initialize AJAX class
new GGM_Commerce_Ajax();