<?php
/**
 * Import REST endpoints.
 *
 * @package PrimeLink
 */

namespace PrimeLink\Core\Rest;

use PrimeLink\Modules\Imports\Service\ImportPreview;
use PrimeLink\Modules\Imports\Parsers\ParserFactory;
use PrimeLink\Modules\Imports\DataSources\UrlSource;
use PrimeLink\Modules\Imports\DataSources\ExistingFileSource;
use PrimeLink\Modules\Imports\DataSources\DropboxSource;
use PrimeLink\Modules\Imports\DataSources\FtpSource;
use PrimeLink\Modules\Imports\DataSources\GoogleDriveSource;
use PrimeLink\Modules\Imports\DataSources\OneDriveSource;
use PrimeLink\Modules\Imports\Service\ImportRunner;
use PrimeLink\Modules\Imports\Service\StatisticsService;
use PrimeLink\Modules\Imports\Service\ImportTrackingService;
use PrimeLink\Modules\Imports\Service\DryRunService;
use PrimeLink\Modules\Imports\Service\PlatformConnectorService;
use PrimeLink\Helpers\EnvatoHelper;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

final class ImportRest {

    public static function register() {
        add_action( 'rest_api_init', array( __CLASS__, 'routes' ) );
    }

    public static function routes() {

        // Index endpoint for namespace
        register_rest_route( 'primelink/v1', '/', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'index' ),
            'permission_callback' => '__return_true', // Public endpoint for discovery
        ) );

        register_rest_route( 'primelink/v1', '/import/create-draft', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'create_draft' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/import/save-step', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'save_step' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
                'step'      => array( 'required' => true, 'type' => 'integer' ),
                // JS sends associative object here.
                'data'      => array(
                    'required' => false,
                    'type'     => 'object',
                ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/handle-source', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'handle_source' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args'                => array(
                'import_id' => array(
                    'required' => false,
                    'type'     => 'integer',
                ),
                'source'    => array(
                    'required' => false,
                    'type'     => 'string',
                ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/dropbox/list', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'dropbox_list' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'path' => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/preview', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'preview_file' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
                'xpath'     => array( 'required' => false, 'type' => 'string' ),
                'page'      => array( 'required' => false, 'type' => 'integer' ),
                'per_page'  => array( 'required' => false, 'type' => 'integer' ),
                'filters'   => array( 'required' => false, 'type' => 'array' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/mapping-preview', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'mapping_preview' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
                'index'     => array( 'required' => false, 'type' => 'integer' ),
                'filters'   => array( 'required' => false, 'type' => 'array' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/templates', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'list_templates' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'type' => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/list', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'list_imports' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'type' => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/reload', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'reload_import' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/templates/save', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'save_template' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'name' => array( 'required' => true, 'type' => 'string' ),
                'data' => array( 'required' => true, 'type' => 'object' ),
                'type' => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/templates/load', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'load_template' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                // For loading by template name (classic behavior).
                'name'      => array( 'required' => false, 'type' => 'string' ),
                // For loading mapping from a specific import draft (used by Reload Import & UI auto-load).
                'import_id' => array( 'required' => false, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/templates/delete', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'delete_template' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'name' => array( 'required' => true, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/filter-presets', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'list_filter_presets' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/import/filter-presets/save', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'save_filter_preset' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'name'    => array( 'required' => true, 'type' => 'string' ),
                'filters' => array( 'required' => true, 'type' => 'array' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/filter-presets/load', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'load_filter_preset' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'name' => array( 'required' => true, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/filter-presets/delete', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'delete_filter_preset' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'name' => array( 'required' => true, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/run/start', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'run_start' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/run/pause', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'run_pause' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/run/resume', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'run_resume' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/schedule', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'schedule_import' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
                'run_at'    => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/cron/tick', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'cron_tick' ),
            'permission_callback' => '__return_true',
            'args' => array(
                'token'     => array( 'required' => true, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/run/status', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'run_status' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/ai/test', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'ai_test' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/import/delete-products', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'delete_products' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/delete-draft', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'delete_draft' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/log', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'get_log' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/anomalies', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'get_anomalies' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/xml-roots', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'xml_roots' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/xml-tags', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'xml_tags' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
                'root'      => array( 'required' => false, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/statistics', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'get_statistics' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'date_from' => array( 'required' => false, 'type' => 'string' ),
                'date_to'   => array( 'required' => false, 'type' => 'string' ),
                'import_id' => array( 'required' => false, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/rollback', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'rollback_import' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id'       => array( 'required' => true, 'type' => 'integer' ),
                'delete_created'  => array( 'required' => false, 'type' => 'boolean' ),
                'restore_updated' => array( 'required' => false, 'type' => 'boolean' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/rollback-summary', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'get_rollback_summary' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/dry-run', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'dry_run_import' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'import_id'         => array( 'required' => true, 'type' => 'integer' ),
                'limit'             => array( 'required' => false, 'type' => 'integer' ),
                'validate_required' => array( 'required' => false, 'type' => 'boolean' ),
                'validate_format'   => array( 'required' => false, 'type' => 'boolean' ),
                'check_duplicates'  => array( 'required' => false, 'type' => 'boolean' ),
                'skip_unchanged'    => array( 'required' => false, 'type' => 'boolean' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/platform/connectors', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'list_connectors' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/platform/test-connection', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'test_platform_connection' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'connector'   => array( 'required' => true, 'type' => 'string' ),
                'credentials' => array( 'required' => true, 'type' => 'object' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/platform/fetch', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'fetch_platform_data' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'connector'   => array( 'required' => true, 'type' => 'string' ),
                'credentials' => array( 'required' => true, 'type' => 'object' ),
                'data_type'   => array( 'required' => true, 'type' => 'string' ),
                'limit'       => array( 'required' => false, 'type' => 'integer' ),
                'page'        => array( 'required' => false, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/webhook/(?P<import_id>\d+)', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'handle_webhook' ),
            'permission_callback' => array( __CLASS__, 'webhook_permission' ), // Verify webhook token
            'args' => array(
                'import_id' => array( 'required' => true, 'type' => 'integer' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/import/uploads', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'list_uploads' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'get_settings' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings/save', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'save_settings' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings/notify-test', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'notify_test' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings/test-license', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'test_license' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings/deactivate-license', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'deactivate_license' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/settings/check-updates', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'check_updates' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/import/get-draft', array(
        'methods'             => 'POST',
        'callback'            => array( __CLASS__, 'get_draft' ),
        'permission_callback' => array( __CLASS__, 'permission' ),
        'args' => array(
            'import_id' => array( 'required' => true, 'type' => 'integer' ),
        ),
         ) );

        // Activation Wizard endpoints
        register_rest_route( 'primelink/v1', '/wizard/verify-license', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_verify_license' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
            'args' => array(
                'purchase_code' => array( 'required' => true, 'type' => 'string' ),
            ),
        ) );

        register_rest_route( 'primelink/v1', '/wizard/save-setup', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_save_setup' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/wizard/test-system', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_test_system' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/wizard/complete', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_complete' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/wizard/restart', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_restart' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/wizard/skip', array(
            'methods'             => 'POST',
            'callback'            => array( __CLASS__, 'wizard_skip' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        // Developer API endpoints
        register_rest_route( 'primelink/v1', '/system/status', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'system_status' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );

        register_rest_route( 'primelink/v1', '/system/health', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'health_check' ),
            'permission_callback' => '__return_true', // Public endpoint
        ) );

        register_rest_route( 'primelink/v1', '/system/info', array(
            'methods'             => 'GET',
            'callback'            => array( __CLASS__, 'system_info' ),
            'permission_callback' => array( __CLASS__, 'permission' ),
        ) );


    }

    /**
     * Check permissions and rate limit for REST API requests.
     *
     * @param \WP_REST_Request $request Request object.
     * @return bool|WP_Error
     */
    /**
     * Permission callback for REST API endpoints.
     * Envato compliant: Verifies user capabilities and nonce.
     *
     * @param WP_REST_Request|null $request Request object.
     * @return bool|WP_Error
     */
    public static function permission( $request = null ) {
        // Check user capabilities first (filterable for WooCommerce/Envato compliance).
        // Default to manage_woocommerce if WooCommerce is active, otherwise manage_options.
        $default_capability = class_exists( 'WooCommerce' ) ? 'manage_woocommerce' : 'manage_options';
        $capability         = apply_filters( 'mt_primelink_capability', $default_capability );
        if ( ! current_user_can( $capability ) ) {
            return new \WP_Error(
                'rest_forbidden',
                __( 'You are not allowed to manage PrimeLink imports.', 'primelink' ),
                array( 'status' =>  rest_authorization_required_code() )
            );
        }

        // For POST/PUT/DELETE requests, verify nonce explicitly (Envato compliance)
        if ( $request instanceof \WP_REST_Request ) {
            $method = $request->get_method();
            if ( in_array( $method, array( 'POST', 'PUT', 'DELETE', 'PATCH' ), true ) ) {
                $nonce = $request->get_header( 'X-WP-Nonce' );
                if ( ! $nonce || ! wp_verify_nonce( $nonce, 'wp_rest' ) ) {
                    return new \WP_Error(
                        'rest_forbidden',
                        __( 'Invalid nonce. Please refresh the page and try again.', 'primelink' ),
                        array( 'status' => 403 )
                    );
                }
            }

            // Apply rate limiting (exclude polling endpoints for active imports)
            $settings = get_option( 'mt_primelink_settings', array() );
            $rate_limit_enabled = $settings['rate_limit_enabled'] ?? true;
            // Increased default limits to support multiple concurrent imports
            $rate_limit_max = absint( $settings['rate_limit_max'] ?? 500 ); // Increased from 100 to 500
            $rate_limit_window = absint( $settings['rate_limit_window'] ?? 60 );

            // Exclude polling endpoints from rate limiting to allow multiple concurrent imports
            $route = $request->get_route();
            $polling_endpoints = array(
                '/import/run/status',    // Status polling
                '/import/log',            // Log polling
                '/import/anomalies',      // Anomalies polling
                '/settings',              // Settings (read-only, low impact)
            );
            
            $is_polling_endpoint = false;
            foreach ( $polling_endpoints as $endpoint ) {
                if ( strpos( $route, $endpoint ) !== false ) {
                    $is_polling_endpoint = true;
                    break;
                }
            }

            // Only apply rate limiting to non-polling endpoints
            if ( $rate_limit_enabled && ! $is_polling_endpoint ) {
                $rate_limit_result = \PrimeLink\Helpers\RateLimiterHelper::check_request(
                    $request,
                    array(
                        'limit'    => $rate_limit_max,
                        'interval' => $rate_limit_window,
                        'policy'   => 'fixed_window',
                    )
                );

                // If rate limited, return error
                if ( is_wp_error( $rate_limit_result ) ) {
                    return $rate_limit_result;
                }
            }
        }

        return true;
    }

    /**
     * Verify webhook token for public webhook endpoint.
     * 
     * @param WP_REST_Request $request Request object.
     * @return bool|WP_Error
     */
    public static function webhook_permission( $request ) {
        $import_id = absint( $request['import_id'] );
        
        if ( $import_id < 1 ) {
            return false;
        }

        // Get webhook token from header or query parameter
        $token = $request->get_header( 'X-Webhook-Token' );
        if ( empty( $token ) ) {
            $token = $request->get_param( 'token' );
        }
        $token = sanitize_text_field( $token );

        // Get stored webhook token for this import
        $draft = ImportRunner::get_draft( $import_id );
        if ( empty( $draft ) ) {
            return false;
        }

        // Check if webhook token is set in import settings
        $webhook_token = $draft['step_data'][2]['webhook_token'] ?? '';
        
        // If no token is configured, default to deny for security.
        // Allowing this can be explicitly opted-in via filter if needed.
        if ( empty( $webhook_token ) ) {
            $allow_without_token = (bool) apply_filters( 'mt_primelink_allow_webhook_without_token', false, $import_id, $request );
            if ( ! $allow_without_token ) {
                return new \WP_Error(
                    'rest_forbidden',
                    __( 'Webhook token is not configured for this import.', 'primelink' ),
                    array( 'status' => 403 )
                );
            }

            // If explicitly allowed, log a warning for transparency.
            \PrimeLink\Helpers\LoggerHelper::warning(
                'Webhook called without configured token but allowed via filter.',
                array( 'import_id' => $import_id )
            );
            return true;
        }

        // Verify token matches
        if ( empty( $token ) || ! hash_equals( $webhook_token, $token ) ) {
            return new \WP_Error(
                'rest_forbidden',
                __( 'Invalid webhook token.', 'primelink' ),
                array( 'status' => 403 )
            );
        }

        return true;
    }

    public static function run_start( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Missing import ID.' ), 400 );
        }
        try {
            // Use ImportRunner for both products and orders
            // ImportRunner::tick() already handles order imports correctly
            $state = ImportRunner::start( $import_id );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response( array(
                'message' => __( 'Failed to start import:', 'primelink' ) . ' ' . $e->getMessage(),
            ), 500 );
        }
        return $state;
    }

    public static function run_pause( $request ) {
        $import_id = absint( $request['import_id'] );
        return ImportRunner::pause( $import_id );
    }

    public static function run_resume( $request ) {
        $import_id = absint( $request['import_id'] );
        return ImportRunner::resume( $import_id );
    }

    public static function run_status( $request ) {
        $import_id = absint( $request['import_id'] );
        try {
            return ImportRunner::status( $import_id );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response( array(
                'message' => __( 'Failed to fetch status:', 'primelink' ) . ' ' . $e->getMessage(),
            ), 500 );
        }
    }

    /** ---------------- AI Test ---------------- */
    public static function ai_test( $request ) {
        $api_key = sanitize_text_field( $request['api_key'] ?? '' );
        $model   = sanitize_text_field( $request['model'] ?? '' );

        $settings = self::get_settings();
        if ( empty( $api_key ) ) {
            $api_key = $settings['ai_key'] ?? '';
        }
        if ( empty( $model ) ) {
            $model = $settings['ai_model'] ?? 'gpt-4o-mini';
        }

        if ( empty( $api_key ) ) {
            return new \WP_REST_Response( array( 'message' => 'API key is required to test AI.' ), 400 );
        }

        return array(
            'ok'      => true,
            'model'   => $model,
            'message' => 'AI key stored, ready for semantic suggestions.',
        );
    }

    public static function schedule_import( $request ) {
        $import_id = absint( $request['import_id'] );
        $run_at    = isset( $request['run_at'] ) ? sanitize_text_field( $request['run_at'] ) : '';

        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Import ID is required.' ), 400 );
        }

        if ( $run_at ) {
            $timestamp = strtotime( $run_at );
            if ( ! $timestamp || $timestamp < time() ) {
                return new \WP_REST_Response( array( 'message' => 'Run time must be in the future.' ), 400 );
            }
            wp_schedule_single_event( $timestamp, 'mt_primelink_import_start_scheduled', array( $import_id ) );
            // Persist next run for external cron fallback.
            $drafts = get_option( 'mt_primelink_import_drafts', array() );
            if ( isset( $drafts[ $import_id ]['step_data'][6]['schedule'] ) ) {
                $drafts[ $import_id ]['step_data'][6]['schedule']['next_run'] = $timestamp;
                update_option( 'mt_primelink_import_drafts', $drafts, false );
            }
            return array(
                'scheduled' => true,
                'run_at'    => date_i18n( 'Y-m-d H:i:s', $timestamp ),
                'recurring' => false,
            );
        }

        // If no explicit time provided, use draft schedule config (step 6).
        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        $draft  = $drafts[ $import_id ] ?? array();
        $schedule = $draft['step_data'][6]['schedule'] ?? array();
        $enabled  = ! empty( $schedule['enabled'] );
        if ( ! $enabled ) {
            return new \WP_REST_Response( array( 'message' => 'Scheduling is not enabled in step 6.' ), 400 );
        }
        $interval = isset( $schedule['interval'] ) ? $schedule['interval'] : 'daily';
        $start    = isset( $schedule['start_time'] ) ? $schedule['start_time'] : '';

        $start_ts = $start ? strtotime( 'today ' . $start ) : time();
        if ( $start_ts < time() ) {
            $start_ts = time() + 60; // next minute
        }

        $recurrence = in_array( $interval, array( 'hourly', 'twicedaily', 'daily', 'weekly', 'monthly' ), true ) ? $interval : 'daily';

        // Clear existing scheduled events for this import
        $crons = _get_cron_array();
        if ( is_array( $crons ) ) {
            foreach ( $crons as $timestamp => $hooks ) {
                if ( isset( $hooks['mt_primelink_import_start_scheduled'] ) ) {
                    foreach ( $hooks['mt_primelink_import_start_scheduled'] as $sig => $data ) {
                        if ( isset( $data['args'][0] ) && absint( $data['args'][0] ) === $import_id ) {
                            wp_unschedule_event( $timestamp, 'mt_primelink_import_start_scheduled', array( $import_id ) );
                        }
                    }
                }
            }
        }

        wp_schedule_event( $start_ts, $recurrence, 'mt_primelink_import_start_scheduled', array( $import_id ) );
        // Persist next run for external cron fallback.
        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( isset( $drafts[ $import_id ]['step_data'][6]['schedule'] ) ) {
            $drafts[ $import_id ]['step_data'][6]['schedule']['next_run'] = $start_ts;
            $drafts[ $import_id ]['step_data'][6]['schedule']['interval'] = $recurrence;
            update_option( 'mt_primelink_import_drafts', $drafts, false );
        }

        return array(
            'scheduled' => true,
            'run_at'    => date_i18n( 'Y-m-d H:i:s', $start_ts ),
            'recurring' => $recurrence,
        );
    }

    public static function cron_tick( $request ) {
        $token     = sanitize_text_field( $request['token'] ?? '' );

        $settings = self::get_settings();
        $saved_token = $settings['cron_token'] ?? '';
        $mode        = $settings['cron_mode'] ?? 'wp';

        if ( $mode !== 'external' ) {
            return new \WP_REST_Response( array( 'message' => 'External cron disabled.' ), 400 );
        }
        if ( empty( $saved_token ) || $token !== $saved_token ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid cron token.' ), 403 );
        }

        try {
            // When running in external mode, also trigger WP-Cron queue so scheduled imports start.
            if ( function_exists( 'wp_cron' ) ) {
                wp_cron();
            }
            ImportRunner::tick_all();
            return array( 'message' => 'Cron ticked all running imports.' );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response( array( 'message' => $e->getMessage() ), 500 );
        }
    }

    public static function get_log( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
        }
        $state = get_option( 'mt_primelink_import_state_' . $import_id, array() );
        $log   = isset( $state['log'] ) && is_array( $state['log'] ) ? $state['log'] : array();
        return array( 'log' => $log );
    }

    public static function get_anomalies( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
        }
        
        $anomalies_data = get_post_meta( $import_id, '_mt_primelink_anomalies', true );
        if ( ! is_array( $anomalies_data ) ) {
            $anomalies_data = array();
        }
        
        return array( 
            'anomalies' => $anomalies_data,
            'count' => count( $anomalies_data ),
        );
    }

    public static function delete_products( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
        }

        $deleted_products    = 0;
        $deleted_attachments = 0;

        $paged = 1;
        do {
            $q = new \WP_Query( array(
                'post_type'      => 'product',
                'post_status'    => 'any',
                'posts_per_page' => 100,
                'paged'          => $paged,
                'fields'         => 'ids',
                'meta_query'     => array(
                    array(
                        'key'   => '_mt_primelink_import_id',
                        'value' => $import_id,
                    ),
                ),
            ) );

            if ( empty( $q->posts ) ) {
                break;
            }

            foreach ( $q->posts as $pid ) {
                $pid = absint( $pid );
                $thumb_id = absint( get_post_thumbnail_id( $pid ) );

                $gallery_ids = array();
                $gallery_meta = get_post_meta( $pid, '_product_image_gallery', true );
                if ( $gallery_meta ) {
                    $gallery_ids = array_filter( array_map( 'absint', explode( ',', $gallery_meta ) ) );
                }

                foreach ( array_filter( array_merge( array( $thumb_id ), $gallery_ids ) ) as $att_id ) {
                    if ( wp_delete_attachment( $att_id, true ) ) {
                        $deleted_attachments++;
                    }
                }

                if ( wp_delete_post( $pid, true ) ) {
                    $deleted_products++;
                }
            }

            $paged++;
        } while ( $q->max_num_pages >= $paged );

        return array(
            'deleted_products'    => $deleted_products,
            'deleted_attachments' => $deleted_attachments,
        );
    }

    public static function delete_draft( $request ) {
        $import_id = absint( $request['import_id'] );

        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( isset( $drafts[ $import_id ] ) ) {
            unset( $drafts[ $import_id ] );
            update_option( 'mt_primelink_import_drafts', $drafts, false );
        }

        // Clean runner state if exists
        delete_option( 'mt_primelink_import_state_' . $import_id );

        return array(
            'deleted'   => true,
            'import_id' => $import_id,
        );
    }

    /** ---------------- XML helpers (roots & tags) ---------------- */
    public static function xml_roots( $request ) {
        $import_id = absint( $request['import_id'] );
        $file      = self::resolve_file_from_draft( $import_id );
        if ( is_wp_error( $file ) ) {
            return $file;
        }

        $roots = \PrimeLink\Modules\Imports\Parsers\XmlHelper::scan_roots( $file['path'], array( 'limit' => 200 ) );
        return array( 'roots' => $roots );
    }

    public static function xml_tags( $request ) {
        $import_id = absint( $request['import_id'] );
        $root      = sanitize_text_field( $request['root'] ?? '' );
        $file      = self::resolve_file_from_draft( $import_id );
        if ( is_wp_error( $file ) ) {
            return $file;
        }

        $tags = \PrimeLink\Modules\Imports\Parsers\XmlHelper::scan_tags_under_root( $file['path'], $root, array( 'limit' => 300 ) );
        return array( 'tags' => $tags );
    }

    /** ---------------- Statistics & Analytics ---------------- */
    public static function get_statistics( $request ) {
        try {
            $args = array(
                'date_from' => sanitize_text_field( $request['date_from'] ?? '' ),
                'date_to'   => sanitize_text_field( $request['date_to'] ?? '' ),
                'import_id' => absint( $request['import_id'] ?? 0 ),
                'type'      => sanitize_text_field( $request['type'] ?? '' ),
            );

            $stats = StatisticsService::get_statistics( $args );
            return new \WP_REST_Response( $stats, 200 );
        } catch ( \Exception $e ) {
            return new \WP_REST_Response( array(
                'message' => 'Failed to load statistics: ' . $e->getMessage(),
                'error'   => $e->getMessage(),
            ), 500 );
        }
    }

    /** ---------------- Rollback/Undo ---------------- */
    public static function rollback_import( $request ) {
        $import_id       = absint( $request['import_id'] );
        $delete_created  = isset( $request['delete_created'] ) ? (bool) $request['delete_created'] : true;
        $restore_updated = isset( $request['restore_updated'] ) ? (bool) $request['restore_updated'] : true;

        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
        }

        if ( ! ImportTrackingService::can_rollback( $import_id ) ) {
            return new \WP_REST_Response( array( 'message' => 'No items to rollback for this import.' ), 404 );
        }

        try {
            $results = ImportTrackingService::rollback( $import_id, $delete_created, $restore_updated );
            return array(
                'success'   => true,
                'message'   => sprintf(
                    'Rollback completed. Deleted: %d, Restored: %d, Errors: %d',
                    $results['deleted'],
                    $results['restored'],
                    $results['errors']
                ),
                'results'   => $results,
            );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response( array(
                'message' => 'Rollback failed: ' . $e->getMessage(),
                'error'   => $e->getMessage(),
            ), 500 );
        }
    }

    public static function get_rollback_summary( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
        }

        $summary = ImportTrackingService::get_rollback_summary( $import_id );
        $can_rollback = ImportTrackingService::can_rollback( $import_id );

        return array(
            'can_rollback' => $can_rollback,
            'summary'      => $summary,
        );
    }

    /** ---------------- Platform Connectors ---------------- */
    public static function list_connectors( $request ) {
        $connectors = PlatformConnectorService::get_connectors();
        $list = array();

        foreach ( $connectors as $name => $connector ) {
            $fields = $connector->get_credentials_fields();
            // Ensure fields is properly formatted as associative array
            // WooCommerce REST API will serialize this correctly
            $list[] = array(
                'name'         => $name,
                'display_name' => $connector->get_display_name(),
                'fields'       => $fields, // This is already an associative array
            );
        }

        return new \WP_REST_Response( array(
            'connectors' => $list,
        ), 200 );
    }

    public static function test_platform_connection( $request ) {
        $connector_name = sanitize_text_field( $request['connector'] );
        $credentials    = (array) $request['credentials'];

        if ( empty( $connector_name ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'message' => 'Connector name required.',
            ), 400 );
        }

        $result = PlatformConnectorService::test_connection( $connector_name, $credentials );
        return $result;
    }

    public static function fetch_platform_data( $request ) {
        $connector_name = sanitize_text_field( $request['connector'] );
        $credentials    = (array) $request['credentials'];
        $data_type      = sanitize_text_field( $request['data_type'] );
        $options        = array(
            'limit' => isset( $request['limit'] ) ? absint( $request['limit'] ) : 10,
        );

        // Shopify doesn't support 'page' parameter, use 'since_id' instead
        // For other platforms, use 'page' if provided
        if ( $connector_name !== 'shopify' ) {
            $options['page'] = isset( $request['page'] ) ? absint( $request['page'] ) : 1;
        }

        if ( empty( $connector_name ) || empty( $data_type ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => 'Missing required parameters.',
            ), 400 );
        }

        // Validate credentials before fetching
        if ( empty( $credentials ) || ! is_array( $credentials ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => 'Invalid credentials: Credentials are empty or not an array.',
            ), 400 );
        }

        // For BigCommerce, ensure access_token is present and not empty
        if ( $connector_name === 'bigcommerce' ) {
            $access_token = isset( $credentials['access_token'] ) ? trim( $credentials['access_token'] ) : '';
            if ( empty( $access_token ) ) {
                return new \WP_REST_Response( array(
                    'success' => false,
                    'error'   => 'Missing or empty access token. Please check your BigCommerce API credentials.',
                    'debug'   => array(
                        'has_credentials' => ! empty( $credentials ),
                        'credentials_keys' => array_keys( $credentials ),
                    ),
                ), 400 );
            }
        }

        $result = PlatformConnectorService::fetch_data( $connector_name, $credentials, $data_type, $options );

        // Convert first item to preview format
        if ( $result['success'] && ! empty( $result['data'] ) ) {
            $first_item = $result['data'][0] ?? array();
            if ( ! empty( $first_item ) ) {
                $converted = PlatformConnectorService::convert_data( $connector_name, $first_item, $data_type );
                $result['preview'] = $converted;
            }
        }

        return $result;
    }

    public static function handle_webhook( $request ) {
        $import_id = absint( $request['import_id'] );
        $payload   = $request->get_json_params();

        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => 'Invalid import ID.',
            ), 400 );
        }

        $result = PlatformConnectorService::process_webhook( $import_id, $payload );
        return $result;
    }

    /** ---------------- Dry Run / Test Mode ---------------- */
    public static function dry_run_import( $request ) {
        $import_id = absint( $request['import_id'] );
        if ( $import_id < 1 ) {
            return new \WP_REST_Response( array( 
                'success' => false,
                'message' => 'Invalid import ID.' 
            ), 400 );
        }

        $options = array(
            'limit'             => isset( $request['limit'] ) ? absint( $request['limit'] ) : 10,
            'validate_required' => isset( $request['validate_required'] ) ? (bool) $request['validate_required'] : true,
            'validate_format'   => isset( $request['validate_format'] ) ? (bool) $request['validate_format'] : true,
            'check_duplicates'  => isset( $request['check_duplicates'] ) ? (bool) $request['check_duplicates'] : true,
            'skip_unchanged'    => isset( $request['skip_unchanged'] ) ? (bool) $request['skip_unchanged'] : false,
        );

        try {
            $results = DryRunService::run( $import_id, $options );
            if ( ! isset( $results['success'] ) ) {
                $results['success'] = true;
            }
            return $results;
        } catch ( \Throwable $e ) {
            \PrimeLink\Helpers\LoggerHelper::exception( $e, array( 'context' => 'Dry Run' ) );
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => 'Dry run failed: ' . $e->getMessage(),
                'trace'   => defined( 'WP_DEBUG' ) && WP_DEBUG ? $e->getTraceAsString() : '',
            ), 500 );
        }
    }

    /**
     * List files inside uploads/primelink (common feed extensions).
     */
    public static function list_uploads( $request ) {
        $upload_dir = wp_upload_dir();
        $base       = trailingslashit( $upload_dir['basedir'] ) . 'primelink';

        if ( ! is_dir( $base ) ) {
            return rest_ensure_response( array( 'files' => array() ) );
        }

        $extensions = array( 'csv', 'xml', 'xlsx', 'xls', 'txt', 'json' );

        $files = array();
        $iterator = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator( $base, \FilesystemIterator::SKIP_DOTS ),
            \RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ( $iterator as $file_info ) {
            if ( $file_info->isDir() ) {
                continue;
            }
            $ext = strtolower( $file_info->getExtension() );
            if ( ! in_array( $ext, $extensions, true ) ) {
                continue;
            }
            $full = $file_info->getPathname();
            $files[] = array(
                'path'  => str_replace( $upload_dir['basedir'], '/wp-content/uploads', $full ),
                'mtime' => $file_info->getMTime(),
                'size'  => $file_info->getSize(),
            );
        }

        usort(
            $files,
            function ( $a, $b ) {
                return ( $b['mtime'] ?? 0 ) <=> ( $a['mtime'] ?? 0 );
            }
        );

        $files = array_slice( $files, 0, 100 );

        return rest_ensure_response( array( 'files' => $files ) );
    }

    private static function resolve_file_from_draft( $import_id ) {
        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( empty( $drafts[ $import_id ] ) ) {
            return new \WP_REST_Response( array( 'message' => 'Draft not found.' ), 404 );
        }

        $draft = $drafts[ $import_id ];
        $file  = $draft['step_data'][2]['file'] ?? null;
        if ( empty( $file['path'] ) ) {
            $file = $draft['file'] ?? null;
        }
        if ( empty( $file['path'] ) && ! empty( $draft['step_data'] ) && is_array( $draft['step_data'] ) ) {
            foreach ( $draft['step_data'] as $step_data ) {
                if ( ! empty( $step_data['file']['path'] ) ) {
                    $file = $step_data['file'];
                    break;
                }
            }
        }
        if ( empty( $file['path'] ) ) {
            return new \WP_REST_Response( array( 'message' => 'No file selected.' ), 404 );
        }
        return $file;
    }

    /** ---------------- Settings ---------------- */
    private static function get_settings_option_key() {
        return 'mt_primelink_settings';
    }

    public static function get_settings() {
        $settings = get_option( self::get_settings_option_key(), array() );
        if ( ! is_array( $settings ) ) {
            $settings = array();
        }

        $defaults = array(
            'license_key'   => '',
            'license_status'=> '',
            'enable_beta'   => 0,
            'enable_logs'   => 0,
            'default_iteration_size' => 20,
            'default_speed_opt'      => 0,
            'fast_import_mode'       => 0,
            'notify_email'           => get_option( 'admin_email' ),
            'notify_on_finish'       => 0,
            'notify_on_error'        => 0,
            'notify_phone'           => '',
            'notify_sms'             => 0,
            'notify_whatsapp'        => 0,
            'notify_tpl_finish'      => 'Import #{import_id} finished. Status: {status}. Created: {created}, Updated: {updated}, Skipped: {skipped}, Errors: {errors}',
            'notify_tpl_error'       => 'Import #{import_id} finished with errors: {errors}. Created: {created}, Updated: {updated}, Skipped: {skipped}',
            'notify_builder'         => '',
            'cleanup_days'           => 0,
            'uninstall_delete_data'  => 0,
            'dropbox_access_token'   => '',
            'dropbox_default_path'   => '',
            'gdrive_access_token'    => '',
            'gdrive_default_file'    => '',
            'onedrive_access_token'  => '',
            'onedrive_default_file'  => '',
            'ai_enable'             => 0,
            'ai_key'                => '',
            'ai_model'              => 'gpt-4o-mini',
            'ai_max_tokens'         => 1200,
            'ai_rpm'                => 5,
            'ai_threshold'          => 50,
            'cron_mode'              => 'wp',
            'cron_token'             => '',
            'source_retention_days'  => 0,
            'ftp_protocol'           => 'ftp',
            'ftp_host'               => '',
            'ftp_port'               => '',
            'ftp_user'               => '',
            'ftp_pass'               => '',
            'ftp_passive'            => 1,
            'ftp_root'               => '',
            'ftp_default_file'       => '',
            'platform_connectors'    => array(),
        );

        $merged = array_merge( $defaults, $settings );

        if ( empty( $merged['cron_token'] ) ) {
            $merged['cron_token'] = wp_generate_password( 16, false, false );
            update_option( self::get_settings_option_key(), $merged, false );
        }

        return $merged;
    }

    public static function save_settings( $request ) {
        $key         = sanitize_text_field( $request['license_key'] ?? '' );
        $enable_beta = ! empty( $request['enable_beta'] ) ? 1 : 0;
        $enable_logs = ! empty( $request['enable_logs'] ) ? 1 : 0;
        $default_iteration_size = isset( $request['default_iteration_size'] ) ? max( 1, min( 500, absint( $request['default_iteration_size'] ) ) ) : 20;
        $default_speed_opt      = ! empty( $request['default_speed_opt'] ) ? 1 : 0;
        $fast_import_mode       = ! empty( $request['fast_import_mode'] ) ? 1 : 0;
        $notify_email           = sanitize_email( $request['notify_email'] ?? '' );
        $notify_on_finish       = ! empty( $request['notify_on_finish'] ) ? 1 : 0;
        $notify_on_error        = ! empty( $request['notify_on_error'] ) ? 1 : 0;
        $notify_phone           = sanitize_text_field( $request['notify_phone'] ?? '' );
        $notify_sms             = ! empty( $request['notify_sms'] ) ? 1 : 0;
        $notify_whatsapp        = ! empty( $request['notify_whatsapp'] ) ? 1 : 0;
        $notify_tpl_finish      = sanitize_textarea_field( $request['notify_tpl_finish'] ?? '' );
        $notify_tpl_error       = sanitize_textarea_field( $request['notify_tpl_error'] ?? '' );
        // JSON structure, keep as-is to preserve HTML payload inside blocks (sanitization happens when rendering).
        $notify_builder         = isset( $request['notify_builder'] ) ? (string) $request['notify_builder'] : '';
        $cleanup_days           = isset( $request['cleanup_days'] ) ? max( 0, absint( $request['cleanup_days'] ) ) : 0;
        $uninstall_delete_data  = ! empty( $request['uninstall_delete_data'] ) ? 1 : 0;
        $dropbox_access_token   = sanitize_text_field( $request['dropbox_access_token'] ?? '' );
        $dropbox_default_path   = sanitize_text_field( $request['dropbox_default_path'] ?? '' );
        $gdrive_access_token    = sanitize_text_field( $request['gdrive_access_token'] ?? '' );
        $gdrive_default_file    = sanitize_text_field( $request['gdrive_default_file'] ?? '' );
        $onedrive_access_token  = sanitize_text_field( $request['onedrive_access_token'] ?? '' );
        $onedrive_default_file  = sanitize_text_field( $request['onedrive_default_file'] ?? '' );
        $ai_enable             = ! empty( $request['ai_enable'] ) ? 1 : 0;
        $ai_key                = sanitize_text_field( $request['ai_key'] ?? '' );
        $ai_model              = sanitize_text_field( $request['ai_model'] ?? '' );
        $ai_max_tokens         = isset( $request['ai_max_tokens'] ) ? max( 0, absint( $request['ai_max_tokens'] ) ) : 0;
        $ai_rpm                = isset( $request['ai_rpm'] ) ? max( 0, absint( $request['ai_rpm'] ) ) : 0;
        $ai_threshold          = isset( $request['ai_threshold'] ) ? max( 0, min( 100, absint( $request['ai_threshold'] ) ) ) : 0;
        $cron_mode              = in_array( $request['cron_mode'] ?? 'wp', array( 'wp', 'external' ), true ) ? $request['cron_mode'] : 'wp';
        $cron_token             = sanitize_text_field( $request['cron_token'] ?? '' );
        if ( empty( $cron_token ) ) {
            $cron_token = wp_generate_password( 16, false, false );
        }
        $source_retention_days  = isset( $request['source_retention_days'] ) ? max( 0, absint( $request['source_retention_days'] ) ) : 0;
        $ftp_protocol           = sanitize_text_field( $request['ftp_protocol'] ?? 'ftp' );
        $ftp_host               = sanitize_text_field( $request['ftp_host'] ?? '' );
        $ftp_port               = isset( $request['ftp_port'] ) ? absint( $request['ftp_port'] ) : '';
        $ftp_user               = sanitize_text_field( $request['ftp_user'] ?? '' );
        $ftp_pass               = sanitize_text_field( $request['ftp_pass'] ?? '' );
        $ftp_passive            = ! empty( $request['ftp_passive'] ) ? 1 : 0;
        $ftp_root               = sanitize_text_field( $request['ftp_root'] ?? '' );
        $ftp_default_file       = sanitize_text_field( $request['ftp_default_file'] ?? '' );

        // Platform connectors (can be nested arrays/objects)
        $platform_connectors_raw = $request['platform_connectors'] ?? array();
        if ( $platform_connectors_raw instanceof \stdClass ) {
            $platform_connectors_raw = (array) $platform_connectors_raw;
        }
        $platform_connectors = is_array( $platform_connectors_raw ) ? $platform_connectors_raw : array();

        // Start from existing settings then override keys from request (to avoid wiping unrelated keys)
        $settings = self::get_settings();

        $settings['license_key']    = $key;
        $settings['license_status'] = $key ? 'saved' : ( $settings['license_status'] ?? '' );
        $settings['enable_beta']    = $enable_beta;
        $settings['enable_logs']    = $enable_logs;
        $settings['default_iteration_size'] = $default_iteration_size;
        $settings['default_speed_opt']      = $default_speed_opt;
        $settings['fast_import_mode']       = $fast_import_mode;
        $settings['notify_email']           = $notify_email;
        $settings['notify_on_finish']       = $notify_on_finish;
        $settings['notify_on_error']        = $notify_on_error;
        $settings['notify_phone']           = $notify_phone;
        $settings['notify_sms']             = $notify_sms;
        $settings['notify_whatsapp']        = $notify_whatsapp;
        $settings['notify_tpl_finish']      = $notify_tpl_finish;
        $settings['notify_tpl_error']       = $notify_tpl_error;
        $settings['notify_builder']         = $notify_builder;
        $settings['cleanup_days']           = $cleanup_days;
        $settings['uninstall_delete_data']  = $uninstall_delete_data;
        $settings['dropbox_access_token']   = $dropbox_access_token;
        $settings['dropbox_default_path']   = $dropbox_default_path;
        $settings['gdrive_access_token']    = $gdrive_access_token;
        $settings['gdrive_default_file']    = $gdrive_default_file;
        $settings['onedrive_access_token']  = $onedrive_access_token;
        $settings['onedrive_default_file']  = $onedrive_default_file;
        $settings['ai_enable']              = $ai_enable;
        $settings['ai_key']                 = $ai_key;
        $settings['ai_model']               = $ai_model;
        $settings['ai_max_tokens']          = $ai_max_tokens;
        $settings['ai_rpm']                 = $ai_rpm;
        $settings['ai_threshold']           = $ai_threshold;
        $settings['cron_mode']              = $cron_mode;
        $settings['cron_token']             = $cron_token;
        $settings['source_retention_days']  = $source_retention_days;
        $settings['ftp_protocol']           = $ftp_protocol;
        $settings['ftp_host']               = $ftp_host;
        $settings['ftp_port']               = $ftp_port;
        $settings['ftp_user']               = $ftp_user;
        $settings['ftp_pass']               = $ftp_pass;
        $settings['ftp_passive']            = $ftp_passive;
        $settings['ftp_root']               = $ftp_root;
        $settings['ftp_default_file']       = $ftp_default_file;
        $settings['platform_connectors']    = $platform_connectors;

        // Mirror uninstall preference into dedicated option used by uninstall.php.
        update_option( 'mt_primelink_uninstall_delete_data', $uninstall_delete_data, false );

        // Anomaly Detection Settings
        $settings['anomaly_detection_enabled'] = ! empty( $request['anomaly_detection_enabled'] ) ? 1 : 0;
        $settings['anomaly_detection_config']  = isset( $request['anomaly_detection_config'] ) ? array(
            'price_drop_threshold'      => isset( $request['anomaly_detection_config']['price_drop_threshold'] ) ? max( 0, min( 100, absint( $request['anomaly_detection_config']['price_drop_threshold'] ) ) ) : 50,
            'price_increase_threshold'  => isset( $request['anomaly_detection_config']['price_increase_threshold'] ) ? max( 0, min( 1000, absint( $request['anomaly_detection_config']['price_increase_threshold'] ) ) ) : 200,
            'price_deviation_threshold' => isset( $request['anomaly_detection_config']['price_deviation_threshold'] ) ? max( 0, min( 100, absint( $request['anomaly_detection_config']['price_deviation_threshold'] ) ) ) : 50,
            'max_absolute_price_change' => isset( $request['anomaly_detection_config']['max_absolute_price_change'] ) ? max( 0, floatval( $request['anomaly_detection_config']['max_absolute_price_change'] ) ) : 0,
            'stock_change_threshold'    => isset( $request['anomaly_detection_config']['stock_change_threshold'] ) ? max( 0, min( 100, absint( $request['anomaly_detection_config']['stock_change_threshold'] ) ) ) : 90,
            'max_stock_threshold'       => isset( $request['anomaly_detection_config']['max_stock_threshold'] ) ? max( 0, absint( $request['anomaly_detection_config']['max_stock_threshold'] ) ) : 10000,
            'content_change_threshold'  => isset( $request['anomaly_detection_config']['content_change_threshold'] ) ? max( 0, min( 100, absint( $request['anomaly_detection_config']['content_change_threshold'] ) ) ) : 30,
            'require_sku'               => ! empty( $request['anomaly_detection_config']['require_sku'] ) ? 1 : 0,
            'pause_on_critical'         => ! empty( $request['anomaly_detection_config']['pause_on_critical'] ) ? 1 : 0,
            'detect_script_injection'   => ! empty( $request['anomaly_detection_config']['detect_script_injection'] ) ? 1 : 0,
        ) : array();

        update_option( self::get_settings_option_key(), $settings, false );

        return array(
            'saved'    => true,
            'settings' => $settings,
        );
    }


    /** ---------------- Create Draft ---------------- */
    /**
     * Index endpoint - returns API information.
     *
     * @param \WP_REST_Request $request Request object.
     * @return array
     */
    public static function index( $request ) {
        return array(
            'name'        => 'PrimeLink REST API',
            'version'     => MT_PRIMELINK_VERSION,
            'namespace'   => 'primelink/v1',
            'description' => __( 'REST API endpoints for PrimeLink Import/Export plugin.', 'primelink' ),
            'routes'      => array(
                'import' => array(
                    '/import/create-draft',
                    '/import/handle-source',
                    '/import/preview',
                    '/import/save-step',
                    '/import/templates',
                    '/import/run/start',
                    '/import/run/status',
                    '/import/uploads',
                    '/import/get-draft',
                ),
                'export' => array(
                    '/export/create-draft',
                    '/export/get-draft',
                    '/export/preview',
                    '/export/run/start',
                    '/export/download',
                ),
                'wizard' => array(
                    '/wizard/verify-license',
                    '/wizard/save-setup',
                    '/wizard/test-system',
                    '/wizard/complete',
                    '/wizard/restart',
                ),
                'settings' => array(
                    '/settings',
                    '/settings/save',
                ),
            ),
        );
    }

    public static function create_draft() {
        $drafts = get_option( 'mt_primelink_import_drafts', array() );

        $id = time();
        $drafts[ $id ] = array(
            'created_at'    => current_time( 'mysql' ),
            'step_data'     => array(),
            'current_step'  => 1,
        );

        update_option( 'mt_primelink_import_drafts', $drafts, false );

        return array( 'import_id' => $id );
    }

    /** ---------------- Save Step ---------------- */
    public static function save_step( $request ) {
        $import_id = absint( $request['import_id'] );
        $step      = absint( $request['step'] );
        $data      = (array) ( $request['data'] ?? array() );

        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( empty( $drafts[ $import_id ] ) ) {
            return new \WP_REST_Response( array( 'message' => 'Draft not found.' ), 404 );
        }

        if ( empty( $drafts[ $import_id ]['step_data'] ) ) {
            $drafts[ $import_id ]['step_data'] = array();
        }

        $prev = $drafts[ $import_id ]['step_data'][ $step ] ?? array();
        if ( is_array( $prev ) && is_array( $data ) ) {
            $drafts[ $import_id ]['step_data'][ $step ] = array_replace_recursive( $prev, $data );
        } else {
            $drafts[ $import_id ]['step_data'][ $step ] = $data;
        }

        // If step 1 was saved with a type, set it on the draft root for easy access
        if ( 1 === $step && isset( $data['type'] ) ) {
            $drafts[ $import_id ]['type'] = sanitize_text_field( $data['type'] );
        }

        // If step 6 scheduling was saved, persist next_run for cron safety.
        if ( 6 === $step ) {
            $sched = $drafts[ $import_id ]['step_data'][6]['schedule'] ?? array();
            if ( ! empty( $sched['enabled'] ) ) {
                $start = isset( $sched['start_time'] ) ? $sched['start_time'] : '';
                $start_ts = $start ? strtotime( 'today ' . $start ) : time();
                if ( $start_ts < time() ) {
                    $start_ts = time() + 60;
                }
                $drafts[ $import_id ]['step_data'][6]['schedule']['next_run'] = $start_ts;
            }
        }

        $drafts[ $import_id ]['current_step'] = $step;

        update_option( 'mt_primelink_import_drafts', $drafts, false );

        return array( 'saved' => true );
    }

    /** ---------------- Handle Source (upload/url/existing) ---------------- */
    public static function handle_source( $request ) {
        // For FormData, try $_POST first (WooCommerce might populate it)
        // Then try get_param() (works for JSON and registered args)
        // Then try body_params and query_params
        
        $import_id = 0;
        $source    = '';
        
        // Try $_POST first (for FormData)
        if ( ! empty( $_POST['import_id'] ) ) {
            $import_id = absint( $_POST['import_id'] );
        }
        if ( ! empty( $_POST['source'] ) ) {
            $source = sanitize_text_field( $_POST['source'] );
        }
        
        // If not found in $_POST, try get_param() (works for JSON and registered args)
        if ( empty( $import_id ) ) {
            $import_id_param = $request->get_param( 'import_id' );
            if ( ! is_null( $import_id_param ) ) {
                $import_id = absint( $import_id_param );
            }
        }
        if ( empty( $source ) ) {
            $source_param = $request->get_param( 'source' );
            if ( ! is_null( $source_param ) ) {
                $source = sanitize_text_field( $source_param );
            }
        }
        
        // If still not found, try body_params and query_params
        if ( empty( $import_id ) || empty( $source ) ) {
            $body_params = $request->get_body_params();
            $query_params = $request->get_params();
            $params = array_merge( $query_params, $body_params );
            
            if ( empty( $import_id ) && isset( $params['import_id'] ) ) {
                $import_id = absint( $params['import_id'] );
            }
            if ( empty( $source ) && isset( $params['source'] ) ) {
                $source = sanitize_text_field( $params['source'] );
            }
        }


        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        
        // If draft not found, try to create it if import_id is 0
        if ( empty( $drafts[ $import_id ] ) ) {
            // If import_id is 0 or invalid, create a new draft
            if ( $import_id === 0 ) {
                $id = time();
                $drafts[ $id ] = array(
                    'created_at'    => current_time( 'mysql' ),
                    'step_data'     => array(),
                    'current_step'  => 1,
                );
                update_option( 'mt_primelink_import_drafts', $drafts, false );
                $import_id = $id;
            } else {
                return new \WP_REST_Response( array( 
                    'message' => 'Draft not found.',
                    'import_id' => $import_id,
                ), 404 );
            }
        }

        // If source is empty but we have a file, assume it's desktop upload
        if ( empty( $source ) ) {
            $files = $request->get_file_params();
            if ( ! empty( $files['file'] ) ) {
                $source = 'desktop';
            }
        }

        // Get params from request - try multiple methods
        $params = array();
        $body_params = $request->get_body_params();
        $query_params = $request->get_params();
        $merged_params = array_merge( $query_params, $body_params );
        
        // If JSON was sent (Content-Type: application/json), body_params will contain the parsed JSON
        // If form data was sent, check for payload JSON string or direct params
        $payload_raw = $merged_params['payload'] ?? '';
        if ( empty( $payload_raw ) && ! empty( $_POST['payload'] ) ) {
            $payload_raw = $_POST['payload'];
        }
        
        // If payload is a JSON string, decode it
        if ( ! empty( $payload_raw ) && is_string( $payload_raw ) ) {
            $decoded = json_decode( wp_unslash( $payload_raw ), true );
            if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
                $params = $decoded;
                // If source is in payload but not yet set, use it
                if ( empty( $source ) && ! empty( $params['source'] ) ) {
                    $source = sanitize_text_field( $params['source'] );
                }
            }
        }
        
        // If params is still empty or incomplete, use merged_params directly
        // This handles both JSON body and form data cases
        if ( empty( $params ) || ( empty( $params['protocol'] ) && empty( $params['host'] ) ) ) {
            $params = $merged_params;
            
            // If source is in params but not yet set, use it
            if ( empty( $source ) && ! empty( $params['source'] ) ) {
                $source = sanitize_text_field( $params['source'] );
            }
        }

        switch ( $source ) {

            case 'desktop':
                // For REST API, populate $_FILES from request file params
                // DesktopUploadSource expects $_FILES['file']
                $files = $request->get_file_params();
                if ( ! empty( $files['file'] ) ) {
                    // Temporarily populate $_FILES for DesktopUploadSource
                    $original_files = isset( $_FILES ) ? $_FILES : array();
                    $_FILES['file'] = $files['file'];
                }
                
                // Use DesktopUploadSource handler (same as AJAX)
                $handler = new \PrimeLink\Modules\Imports\DataSources\DesktopUploadSource();
                $res = $handler->handle( array() );
                
                // Restore original $_FILES
                if ( isset( $original_files ) ) {
                    $_FILES = $original_files;
                }
                
                // Normalize response format for REST API
                if ( ! isset( $res['error'] ) ) {
                    // Determine file type from extension
                    $ext  = strtolower( pathinfo( $res['path'], PATHINFO_EXTENSION ) );
                    $type = 'csv';
                    if ( 'xml' === $ext ) {
                        $type = 'xml';
                    } elseif ( in_array( $ext, array( 'xlsx', 'xls' ), true ) ) {
                        $type = 'xlsx';
                    } elseif ( 'json' === $ext ) {
                        $type = 'json';
                    }
                    
                    $res['type'] = $type;
                    $res['source'] = 'desktop';
                    $res['uploaded_at'] = current_time( 'mysql' );
                }
                break;

            case 'url':
                $handler = new UrlSource();
                $res = $handler->handle( array( 'url' => $params['url'] ?? '' ) );
                // Normalize legacy keys if needed
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            case 'existing':
                $handler = new ExistingFileSource();
                $res = $handler->handle( array( 'path' => $params['path'] ?? '' ) );
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            case 'dropbox':
                $handler = new DropboxSource( self::get_settings() );
                $res = $handler->handle( array(
                    'path' => $params['path'] ?? '',
                ) );
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            case 'ftp':
                $handler = new FtpSource( self::get_settings() );
                $res = $handler->handle( array(
                    'protocol' => $params['protocol'] ?? '',
                    'host'     => $params['host'] ?? '',
                    'port'     => $params['port'] ?? '',
                    'user'     => $params['user'] ?? '',
                    'pass'     => $params['pass'] ?? '',
                    'root'     => $params['root'] ?? '',
                    'path'     => $params['path'] ?? '',
                    'passive'  => isset( $params['passive'] ) ? (bool) $params['passive'] : null,
                    'test_only' => isset( $params['test_only'] ) ? (bool) $params['test_only'] : false,
                ) );
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            case 'gdrive':
                $handler = new GoogleDriveSource( self::get_settings() );
                $res = $handler->handle( array(
                    'file' => $params['path'] ?? ( $params['file'] ?? '' ),
                ) );
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            case 'onedrive':
                $handler = new OneDriveSource( self::get_settings() );
                $res = $handler->handle( array(
                    'file' => $params['path'] ?? ( $params['file'] ?? '' ),
                ) );
                if ( isset( $res['file_path'] ) && empty( $res['path'] ) ) {
                    $res['path'] = $res['file_path'];
                }
                if ( isset( $res['file_type'] ) && empty( $res['type'] ) ) {
                    $res['type'] = $res['file_type'];
                }
                break;

            default:
                $res = array( 'error' => 'Source not implemented yet.' );
        }

        // No longer auto-converting XLS/XLSX to CSV - we now use PhpSpreadsheet/XlsxParser directly
        // This preserves the original file and allows better Excel support

        // For test_only mode (connection testing), return success/error directly
        if ( isset( $params['test_only'] ) && $params['test_only'] ) {
            if ( isset( $res['error'] ) ) {
                return new \WP_REST_Response( array( 
                    'success' => false,
                    'message' => $res['error']
                ), 400 );
            }
            if ( isset( $res['success'] ) && $res['success'] ) {
                return new \WP_REST_Response( array( 
                    'success' => true,
                    'data' => array( 'message' => $res['message'] ?? 'Connection successful' )
                ), 200 );
            }
            // If neither success nor error, treat as error
            return new \WP_REST_Response( array( 
                'success' => false,
                'message' => 'Unknown error during connection test'
            ), 400 );
        }

        if ( isset( $res['error'] ) ) {
            return new \WP_REST_Response( array( 'message' => $res['error'] ), 400 );
        }

        // ---- SAVE FILE SAFELY IN DRAFT (redundant locations) ----
        if ( empty( $drafts[ $import_id ]['step_data'] ) ) {
            $drafts[ $import_id ]['step_data'] = array();
        }

        // standard location
        $drafts[ $import_id ]['step_data'][2]['file'] = $res;
        // root fallback
        $drafts[ $import_id ]['file'] = $res;

        update_option( 'mt_primelink_import_drafts', $drafts, false );

        return array(
            'file'      => $res,
            'import_id' => $import_id,
        );
    }

    /**
     * Convert XLSX/XLS to CSV so the rest of the pipeline can parse it.
     */
    private static function maybe_convert_spreadsheet_to_csv( $res ) {
        if ( empty( $res['path'] ) || empty( $res['type'] ) ) {
            return $res;
        }
        $type = strtolower( $res['type'] );
        if ( ! in_array( $type, array( 'xlsx', 'xls' ), true ) ) {
            return $res;
        }

        if ( ! class_exists( '\ZipArchive' ) ) {
            $res['error'] = 'PHP ZipArchive extension is missing. Convert the file to CSV and try again.';
            return $res;
        }

        $parser = ParserFactory::make( 'xlsx' );
        $preview = $parser->preview( $res['path'], array( 'max_rows' => 0 ) );

        if ( empty( $preview['headers'] ) && empty( $preview['rows'] ) ) {
            $msg = isset( $preview['message'] ) ? $preview['message'] : 'Spreadsheet conversion failed. Save as CSV and retry.';
            $res['error'] = $msg;
            return $res;
        }

        $rows = array_merge( array( $preview['headers'] ), $preview['rows'] );
        $dir  = dirname( $res['path'] );
        $csv_name = pathinfo( $res['path'], PATHINFO_FILENAME ) . '-converted.csv';
        $csv_path = $dir . DIRECTORY_SEPARATOR . $csv_name;

        $fh = @fopen( $csv_path, 'w' );
        if ( ! $fh ) {
            $res['error'] = 'Unable to create CSV file from spreadsheet.';
            return $res;
        }
        foreach ( $rows as $row ) {
            fputcsv( $fh, $row );
        }
        fclose( $fh );

        $res['path']          = $csv_path;
        $res['type']          = 'csv';
        $res['original_name'] = ( isset( $res['original_name'] ) ? $res['original_name'] . ' (converted)' : basename( $csv_path ) );

        return $res;
    }

    /** ---------------- Dropbox list ---------------- */
    public static function dropbox_list( $request ) {
        $settings = self::get_settings();
        $token    = $settings['dropbox_access_token'] ?? '';
        if ( ! $token ) {
            return new \WP_REST_Response( array( 'message' => 'Dropbox token missing. Add it in Settings → Integrations.' ), 400 );
        }

        $path = isset( $request['path'] ) ? (string) $request['path'] : '';
        // If a file path is provided, use its directory.
        if ( $path && false === strpos( $path, '://' ) ) {
            $parts = explode( '/', trim( $path, '/' ) );
            if ( count( $parts ) > 1 ) {
                array_pop( $parts );
                $path = '/' . implode( '/', $parts );
            } else {
                $path = '';
            }
        }

        $body = wp_json_encode( array(
            'path'      => $path,
            'recursive' => false,
            'limit'     => 100,
        ) );

        $response = \PrimeLink\Helpers\HttpHelper::post( 'https://api.dropboxapi.com/2/files/list_folder', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'application/json',
            ),
            'body'        => json_decode( $body, true ), // HttpHelper will handle JSON encoding
            'timeout'     => 30,
            'retry'       => 1,
            'retry_delay' => 1,
        ) );

        if ( ! $response['success'] ) {
            return new \WP_REST_Response( array( 'message' => $response['error'] ?? __( 'Failed to list Dropbox folder.', 'primelink' ) ), 400 );
        }

        $code = $response['code'];
        $data = json_decode( $response['body'], true );

        if ( $code < 200 || $code > 299 ) {
            $msg = isset( $data['error_summary'] ) ? $data['error_summary'] : 'Dropbox listing failed.';
            return new \WP_REST_Response( array( 'message' => $msg ), 400 );
        }

        $files = array();
        if ( ! empty( $data['entries'] ) && is_array( $data['entries'] ) ) {
            foreach ( $data['entries'] as $entry ) {
                if ( isset( $entry['.tag'] ) && 'file' === $entry['.tag'] ) {
                    $size = isset( $entry['size'] ) ? absint( $entry['size'] ) : 0;
                    $files[] = array(
                        'name'       => $entry['name'] ?? '',
                        'path'       => $entry['path_display'] ?? '',
                        'size'       => $size,
                        'size_human' => size_format( $size ),
                    );
                }
            }
        }

        return array( 'files' => $files );
    }

    /** ---------------- Step 4 Preview ---------------- */
    public static function preview_file( $request ) {
        $import_id = absint( $request['import_id'] );
        $xpath     = isset( $request['xpath'] ) ? (string) $request['xpath'] : '';
        $page      = isset( $request['page'] ) ? max( 1, absint( $request['page'] ) ) : 1;
        $per_page  = isset( $request['per_page'] ) ? max( 1, min( 200, absint( $request['per_page'] ) ) ) : 20;

        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( empty( $drafts[ $import_id ] ) ) {
            return new \WP_REST_Response( array( 'message' => 'Draft not found.' ), 404 );
        }

        $draft = $drafts[ $import_id ];

        // Check if this is a platform import
        $step2_data = $draft['step_data'][2] ?? array();
        // Check both direct source and nested data source (save_step uses array_replace_recursive)
        $source = $step2_data['source'] ?? ($step2_data['data']['source'] ?? '');

        if ( $source === 'platform' ) {
            // Handle platform import preview
            // Check both direct and nested data structure
            $connector_name = $step2_data['connector'] ?? ($step2_data['data']['connector'] ?? '');
            $credentials    = $step2_data['credentials'] ?? ($step2_data['data']['credentials'] ?? array());
            $data_type      = $step2_data['data_type'] ?? ($step2_data['data']['data_type'] ?? 'products');

            if ( empty( $connector_name ) || empty( $credentials ) ) {
                // Try to get credentials from saved settings if not in draft
                if ( empty( $credentials ) && ! empty( $connector_name ) ) {
                    $settings = get_option( 'mt_primelink_settings', array() );
                    $saved_connectors = $settings['platform_connectors'] ?? array();
                    if ( ! empty( $saved_connectors[ $connector_name ] ) ) {
                        $credentials = $saved_connectors[ $connector_name ];
                    }
                }
                
                if ( empty( $connector_name ) || empty( $credentials ) ) {
                    return new \WP_REST_Response( array( 
                        'message' => 'Platform connector not configured.',
                        'debug' => array(
                            'step2_keys' => array_keys($step2_data),
                            'connector' => $connector_name,
                            'has_credentials' => !empty($credentials),
                            'step2_source' => $step2_data['source'] ?? 'not found',
                        )
                    ), 404 );
                }
            }

            // Calculate offset for pagination
            $offset = max( 0, ( $page - 1 ) * $per_page );
            
            // Fetch data from platform (use original data_type, e.g. 'products')
            // For preview, we fetch enough items to cover the current page
            $options = array(
                'limit' => min( 250, $per_page * $page ), // Fetch enough for current page (max 250 for Shopify)
            );

            // For non-Shopify platforms, use page parameter
            if ( $connector_name !== 'shopify' ) {
                $options['page'] = $page;
                $options['limit'] = $per_page;
            }

            $result = PlatformConnectorService::fetch_data( $connector_name, $credentials, $data_type, $options );
            
            // Normalize data_type for convert_data: 'products' -> 'product', 'orders' -> 'order', 'customers' -> 'customer', 'coupons' -> 'coupon'
            $convert_data_type = $data_type;
            if ( $convert_data_type === 'products' ) {
                $convert_data_type = 'product';
            } elseif ( $convert_data_type === 'orders' ) {
                $convert_data_type = 'order';
            } elseif ( $convert_data_type === 'customers' ) {
                $convert_data_type = 'customer';
            } elseif ( $convert_data_type === 'coupons' ) {
                $convert_data_type = 'coupon';
            }

            if ( ! $result['success'] ) {
                return new \WP_REST_Response( array(
                    'message' => 'Failed to fetch data from platform: ' . ( $result['error'] ?? 'Unknown error' ),
                ), 500 );
            }

            // Convert platform data to preview format
            $platform_items = $result['data'] ?? array();
            $headers = array();
            $rows = array();
            $all_keys = array(); // Collect all possible keys from all rows

            foreach ( $platform_items as $platform_item ) {
                // Convert to WooCommerce format (use normalized data_type)
                $woo_data = PlatformConnectorService::convert_data( $connector_name, $platform_item, $convert_data_type );
                
                if ( empty( $woo_data ) || ! is_array( $woo_data ) ) {
                    \PrimeLink\Helpers\LoggerHelper::debug(
                        'Empty or invalid woo_data',
                        array(
                            'connector' => $connector_name,
                            'data_type' => $data_type,
                            'item_keys' => array_keys( $platform_item ?? array() ),
                        )
                    );
                }
                
                // Flatten to row format
                $row = ImportRunner::flatten_woo_data_to_row( $woo_data );
                
                // Ensure row is an array
                if ( ! is_array( $row ) ) {
                    $row = array();
                }
                
                if ( empty( $row ) && ! empty( $woo_data ) ) {
                    \PrimeLink\Helpers\LoggerHelper::debug(
                        'Row is empty after flattening',
                        array(
                            'woo_data_keys' => array_keys( $woo_data ),
                            'woo_data'      => $woo_data,
                        )
                    );
                }
                
                $rows[] = $row;

                // Collect all keys from this row
                // Iterate through row to collect string keys (associative array keys)
                if ( ! empty( $row ) && is_array( $row ) ) {
                    foreach ( $row as $key => $value ) {
                        // Only collect string keys (not numeric indices)
                        // PHP array_keys can return numeric strings like "0", "1" for indexed arrays
                        // We want only associative keys that are actual strings
                        if ( is_string( $key ) && ! is_numeric( $key ) && ! in_array( $key, $all_keys, true ) ) {
                            $all_keys[] = $key;
                        }
                    }
                }
            }

            // Build headers from all collected keys (unique, preserve order)
            if ( ! empty( $all_keys ) ) {
                $headers = array_values( array_unique( $all_keys ) );
            }

            // Convert rows from associative arrays to indexed arrays for consistency with file imports
            // Each row should have values in the same order as headers
            $indexed_rows = array();
            foreach ( $rows as $row ) {
                $indexed_row = array();
                foreach ( $headers as $header ) {
                    $indexed_row[] = isset( $row[ $header ] ) ? $row[ $header ] : '';
                }
                $indexed_rows[] = $indexed_row;
            }
            $rows = $indexed_rows;

            // Apply pagination for Shopify (since we fetched more items than needed)
            if ( $connector_name === 'shopify' && count( $rows ) > $per_page ) {
                $rows = array_slice( $rows, $offset, $per_page );
            }

            $total = $result['total'] ?? count( $platform_items );
            $total_filtered = count( $rows );

            // Apply filters if any
            $filters = $draft['step_data'][4]['filters'] ?? array();
            if ( isset( $request['filters'] ) && is_array( $request['filters'] ) ) {
                $filters = $request['filters'];
            }
            $filters = self::normalize_filters( $filters );
            if ( ! empty( $filters ) ) {
                $rows = self::apply_filters_to_rows( $headers, $rows, $filters );
                $total_filtered = count( $rows );
            }

            // Return in same format as file-based preview
            return array(
                'preview' => array(
                    'headers'          => $headers,
                    'rows'             => $rows,
                    'total_unfiltered' => $total,
                    'total_filtered'   => $total_filtered,
                    'total'            => $total_filtered,
                    'page'             => $page,
                    'per_page'         => $per_page,
                ),
                'type'    => 'platform',
            );
        }

        // File-based import (existing logic)
        // 1) Standard location (step 2)
        $file = $draft['step_data'][2]['file'] ?? null;

        // 2) Root fallback
        if ( empty( $file['path'] ) ) {
            $file = $draft['file'] ?? null;
        }

        // 3) Deep fallback: scan all steps
        if ( empty( $file['path'] ) && ! empty( $draft['step_data'] ) && is_array( $draft['step_data'] ) ) {
            foreach ( $draft['step_data'] as $step_data ) {
                if ( ! empty( $step_data['file']['path'] ) ) {
                    $file = $step_data['file'];
                    break;
                }
            }
        }

        if ( empty( $file['path'] ) ) {
            // If no file found, check if this might be a platform import that wasn't detected
            $step2_check = $draft['step_data'][2] ?? array();
            if ( ! empty( $step2_check['connector'] ) || ! empty( $step2_check['source'] ) ) {
                return new \WP_REST_Response( array( 
                    'message' => 'Platform import detected but not configured correctly.',
                    'debug' => array(
                        'step2_data' => $step2_check,
                        'has_connector' => !empty($step2_check['connector']),
                        'has_source' => !empty($step2_check['source']),
                    )
                ), 404 );
            }
            return new \WP_REST_Response( array( 'message' => 'No file for preview.' ), 404 );
        }

        // Normalize type if legacy key used
        if ( empty( $file['type'] ) && ! empty( $file['file_type'] ) ) {
            $file['type'] = $file['file_type'];
        }

        $parser = ParserFactory::make( $file['type'] );

        $filters = $draft['step_data'][4]['filters'] ?? array();
        if ( isset( $request['filters'] ) && is_array( $request['filters'] ) ) {
            $filters = $request['filters'];
        }
        $filters = self::normalize_filters( $filters );
        $filters = apply_filters( 'primelink_import_filters', $filters, $import_id, $file );

        $args = array(
            'xpath'    => $xpath,
            'max_rows' => 0, // unlimited
            'filters'  => $filters,
        );

        $args = apply_filters( 'primelink_import_parser_args', $args, $import_id, $file );

        // Cache preview results for URL sources (not local files that might change)
        $use_cache = ( $file['source'] ?? '' ) === 'url';
        $cache_key = null;
        
        if ( $use_cache ) {
            $cache_key = sprintf(
                'preview_%s_%s_%s',
                md5( $file['path'] ?? '' ),
                md5( serialize( $args ) ),
                filemtime( $file['path'] ) ?? 0
            );
            
            $cached = \PrimeLink\Helpers\CacheHelper::get( $cache_key );
            if ( $cached !== null ) {
                return $cached;
            }
        }

        try {
            $preview = $parser->preview( $file['path'], $args );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response( array(
                'message' => 'Preview failed: ' . $e->getMessage(),
                'error'   => $e->getMessage(),
                'file'    => $e->getFile(),
                'line'    => $e->getLine(),
            ), 500 );
        }

        /**
         * Allow plugins to adjust the preview payload (e.g. filter rows or add meta).
         *
         * @param array $preview    Parser preview array with headers/rows/total.
         * @param int   $import_id  Current import ID.
         * @param array $file       File descriptor from step 2.
         * @param array $args       Parser args (xpath, filters, max_rows).
         */
        $preview = apply_filters( 'primelink_import_preview_result', $preview, $import_id, $file, $args );

        $headers = isset( $preview['headers'] ) && is_array( $preview['headers'] ) ? $preview['headers'] : array();
        $rows    = isset( $preview['rows'] ) && is_array( $preview['rows'] ) ? $preview['rows'] : array();
        $total_unfiltered = count( $rows );

        if ( ! empty( $filters ) ) {
            $rows = self::apply_filters_to_rows( $headers, $rows, $filters );
        }

        $total_filtered = count( $rows );
        $offset         = max( 0, ( $page - 1 ) * $per_page );
        $rows_page      = array_slice( $rows, $offset, $per_page );

        $preview['headers']          = $headers;
        $preview['rows']             = $rows_page;
        $preview['total_unfiltered'] = $total_unfiltered;
        $preview['total_filtered']   = $total_filtered;
        $preview['total']            = $total_filtered;
        $preview['page']             = $page;
        $preview['per_page']         = $per_page;

        $result = array(
            'type'    => $file['type'],
            'preview' => $preview,
        );

        // Cache successful preview results for URL sources (15 minutes)
        if ( $use_cache && $cache_key ) {
            \PrimeLink\Helpers\CacheHelper::set( $cache_key, $result, 900 ); // 15 minutes
        }

        return $result;
    }

    /** ---------------- Step 5 Mapping Preview ---------------- */
    public static function mapping_preview( $request ) {
        $import_id = absint( $request['import_id'] );
        $index     = isset( $request['index'] ) ? absint( $request['index'] ) : 0;
        $filters   = $request['filters'] ?? null;

        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( empty( $drafts[ $import_id ] ) ) {
            return new \WP_REST_Response( array( 'message' => 'Draft not found.' ), 404 );
        }

        $draft = $drafts[ $import_id ];

        // Check if this is a platform import
        $step2_data = $draft['step_data'][2] ?? array();
        // Check both direct source and nested data source
        $source = $step2_data['source'] ?? ($step2_data['data']['source'] ?? '');

        if ( $source === 'platform' ) {
            // Handle platform import preview
            // Check both direct and nested data structure
            $connector_name = $step2_data['connector'] ?? ($step2_data['data']['connector'] ?? '');
            $credentials    = $step2_data['credentials'] ?? ($step2_data['data']['credentials'] ?? array());
            $data_type      = $step2_data['data_type'] ?? ($step2_data['data']['data_type'] ?? 'products');

            if ( empty( $connector_name ) || empty( $credentials ) ) {
                return new \WP_REST_Response( array( 'message' => 'Platform connector not configured.' ), 404 );
            }

            // Fetch data from platform (use original data_type, e.g. 'products')
            $options = array(
                'limit' => 50, // Get more items for preview
            );
            
            // For Shopify, don't use page parameter
            if ( $connector_name !== 'shopify' ) {
                $options['page'] = isset( $request['page'] ) ? absint( $request['page'] ) : 1;
            }

            $result = PlatformConnectorService::fetch_data( $connector_name, $credentials, $data_type, $options );
            
            // Normalize data_type for convert_data: 'products' -> 'product', 'orders' -> 'order', 'customers' -> 'customer'
            $convert_data_type = $data_type;
            if ( $convert_data_type === 'products' ) {
                $convert_data_type = 'product';
            } elseif ( $convert_data_type === 'orders' ) {
                $convert_data_type = 'order';
            } elseif ( $convert_data_type === 'customers' ) {
                $convert_data_type = 'customer';
            }

            if ( ! $result['success'] ) {
                return new \WP_REST_Response( array(
                    'message' => 'Failed to fetch data from platform: ' . ( $result['error'] ?? 'Unknown error' ),
                ), 500 );
            }

            // Convert platform data to preview format
            $platform_items = $result['data'] ?? array();
            $headers = array();
            $rows = array();
            $all_keys = array(); // Collect all possible keys from all rows

            foreach ( $platform_items as $platform_item ) {
                // Convert to WooCommerce format (use normalized data_type)
                $woo_data = PlatformConnectorService::convert_data( $connector_name, $platform_item, $convert_data_type );
                // Flatten to row format
                $row = ImportRunner::flatten_woo_data_to_row( $woo_data );
                $rows[] = $row;

                // Collect all keys from this row
                if ( ! empty( $row ) && is_array( $row ) ) {
                    $all_keys = array_merge( $all_keys, array_keys( $row ) );
                }
            }

            // Build headers from all collected keys (unique, preserve order)
            if ( ! empty( $all_keys ) ) {
                $headers = array_values( array_unique( $all_keys ) );
            }

            $total = $result['total'] ?? count( $rows );
            $item  = isset( $rows[ $index ] ) ? $rows[ $index ] : ( ! empty( $rows ) ? $rows[0] : array() );

            // Build fields tree (flat structure for platform data)
            $fields_tree = array();
            foreach ( $headers as $header ) {
                $fields_tree[] = array(
                    'name'      => $header,
                    'full'      => $header,
                    'is_field'  => true,
                    'children'  => array(),
                );
            }

            return array(
                'success'     => true,
                'fields'      => $headers,
                'fields_tree' => $fields_tree,
                'item'        => $item,
                'total'       => $total,
                'index'       => $index,
                'type'        => 'platform',
            );
        }

        // File-based import (existing logic)
        // Same robust file lookup as preview_file
        $file = $draft['step_data'][2]['file'] ?? null;
        if ( empty( $file['path'] ) ) {
            $file = $draft['file'] ?? null;
        }
        if ( empty( $file['path'] ) && ! empty( $draft['step_data'] ) && is_array( $draft['step_data'] ) ) {
            foreach ( $draft['step_data'] as $step_data ) {
                if ( ! empty( $step_data['file']['path'] ) ) {
                    $file = $step_data['file'];
                    break;
                }
            }
        }

        if ( empty( $file['path'] ) ) {
            return new \WP_REST_Response( array( 'message' => 'No file selected.' ), 404 );
        }

        $xpath = ! empty( $draft['step_data'][4]['xpath'] )
            ? $draft['step_data'][4]['xpath']
            : '';

        try {
            return ImportPreview::get_mapping_preview(
                $file,
                array(
                    'index'   => $index,
                    'xpath'   => $xpath,
                    'filters' => is_array( $filters ) ? $filters : ( $draft['step_data'][4]['filters'] ?? array() ),
                )
            );
        } catch ( \Throwable $e ) {
            return new \WP_REST_Response(
                array(
                    'message' => 'Mapping preview failed: ' . $e->getMessage(),
                ),
                500
            );
        }
    }

    /**
     * Filters helper for preview endpoints.
     */
    private static function normalize_filters( $filters ) {
        $out = array();
        foreach ( (array) $filters as $f ) {
            $field = isset( $f['field'] ) ? sanitize_text_field( $f['field'] ) : '';
            $op    = isset( $f['op'] ) ? sanitize_text_field( $f['op'] ) : '=';
            $val   = $f['value'] ?? '';
            if ( is_array( $val ) ) {
                $val = implode( ',', $val );
            }
            $val = (string) $val;
            if ( $field === '' && $val === '' ) {
                continue;
            }
            $out[] = array(
                'field' => $field,
                'op'    => $op,
                'value' => $val,
            );
        }
        return $out;
    }

    private static function apply_filters_to_rows( $headers, $rows, $filters ) {
        if ( empty( $filters ) ) {
            return $rows;
        }

        $headers = array_values( (array) $headers );
        $filtered = array();

        foreach ( (array) $rows as $row ) {
            $assoc = array();
            foreach ( $headers as $idx => $h ) {
                $assoc[ $h ] = isset( $row[ $idx ] ) ? (string) $row[ $idx ] : '';
            }
            if ( self::row_matches_filters( $assoc, $filters ) ) {
                $filtered[] = $row;
            }
        }

        return $filtered;
    }

    private static function row_matches_filters( $assoc, $filters ) {
        foreach ( (array) $filters as $f ) {
            $field = $f['field'] ?? '';
            $op    = $f['op'] ?? '=';
            $value = isset( $f['value'] ) ? (string) $f['value'] : '';

            $left = isset( $assoc[ $field ] ) ? trim( (string) $assoc[ $field ] ) : '';
            $list = self::split_values( $value );

            $match = true;
            switch ( $op ) {
                case '!=':
                    $match = ( $left !== $value );
                    break;
                case 'contains':
                    $match = ( stripos( $left, $value ) !== false );
                    break;
                case 'not_contains':
                    $match = ( stripos( $left, $value ) === false );
                    break;
                case 'in':
                    $match = empty( $list ) ? false : in_array( strtolower( $left ), array_map( 'strtolower', $list ), true );
                    break;
                case 'not_in':
                    $match = empty( $list ) ? true : ! in_array( strtolower( $left ), array_map( 'strtolower', $list ), true );
                    break;
                case 'regex':
                case 'not_regex':
                    $pattern = '/' . str_replace( '/', '\/', $value ) . '/i';
                    $ok = @preg_match( $pattern, $left );
                    $match = $op === 'regex' ? ( $ok === 1 ) : ( $ok === 0 || $ok === false );
                    break;
                case '>':
                    $match = floatval( $left ) > floatval( $value );
                    break;
                case '<':
                    $match = floatval( $left ) < floatval( $value );
                    break;
                case '>=':
                    $match = floatval( $left ) >= floatval( $value );
                    break;
                case '<=':
                    $match = floatval( $left ) <= floatval( $value );
                    break;
                case '=':
                default:
                    $match = ( $left === $value );
                    break;
            }

            if ( ! $match ) {
                return false;
            }
        }
        return true;
    }

    private static function split_values( $value ) {
        $parts = preg_split( '/\s*[,|;\n\r]+\s*/', (string) $value );
        $parts = array_values( array_filter( array_map( 'trim', $parts ), function( $v ) { return $v !== ''; } ) );
        return $parts;
    }

    /** ---------------- Templates storage ---------------- */

private static function get_templates_option_key() {
    return 'mt_primelink_import_templates';
}

public static function list_templates( $request ) {
    $templates = get_option( self::get_templates_option_key(), array() );
    if ( ! is_array( $templates ) ) {
        $templates = array();
    }

    // Get type parameter from GET request (query string)
    $type = $request->get_param( 'type' );
    if ( empty( $type ) && isset( $request['type'] ) ) {
        $type = $request['type'];
    }
    $type = sanitize_text_field( $type );
    
    $names = array_keys( $templates );
    
    // Filter by type if provided - strict filtering: only show templates with the correct prefix
    if ( $type === 'order' ) {
        // Show only templates that start with "order:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'order:' ) === 0;
        } );
    } elseif ( $type === 'product' ) {
        // Show only templates that start with "product:" or have no prefix (for backward compatibility with old templates)
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'product:' ) === 0 || ( strpos( $name, 'order:' ) !== 0 && strpos( $name, 'customer:' ) !== 0 && strpos( $name, 'coupon:' ) !== 0 && strpos( $name, 'category:' ) !== 0 && strpos( $name, 'tag:' ) !== 0 && strpos( $name, 'attribute:' ) !== 0 && strpos( $name, 'variation:' ) !== 0 && strpos( $name, 'review:' ) !== 0 && strpos( $name, 'download:' ) !== 0 );
        } );
    } elseif ( $type === 'customer' ) {
        // Show only templates that start with "customer:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'customer:' ) === 0;
        } );
    } elseif ( $type === 'coupon' ) {
        // Show only templates that start with "coupon:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'coupon:' ) === 0;
        } );
    } elseif ( $type === 'category' || $type === 'categories' ) {
        // Show only templates that start with "category:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'category:' ) === 0;
        } );
    } elseif ( $type === 'tag' || $type === 'tags' ) {
        // Show only templates that start with "tag:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'tag:' ) === 0;
        } );
    } elseif ( $type === 'attribute' || $type === 'attributes' ) {
        // Show only templates that start with "attribute:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'attribute:' ) === 0;
        } );
    } elseif ( $type === 'variation' || $type === 'variations' ) {
        // Show only templates that start with "variation:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'variation:' ) === 0;
        } );
    } elseif ( $type === 'review' || $type === 'reviews' ) {
        // Show only templates that start with "review:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'review:' ) === 0;
        } );
    } elseif ( $type === 'download' || $type === 'downloads' ) {
        // Show only templates that start with "download:"
        $names = array_filter( $names, function( $name ) {
            return strpos( $name, 'download:' ) === 0;
        } );
    }
    // If no type specified, return all (for backward compatibility)
    
    $names = array_values( $names );
    sort( $names );

    return array(
        'templates' => $names,
    );
}

public static function save_template( $request ) {
    $name = sanitize_text_field( $request['name'] );
    $data = (array) $request['data'];
    $type = isset( $request['type'] ) ? sanitize_text_field( $request['type'] ) : '';

    if ( empty( $name ) ) {
        return new \WP_REST_Response( array( 'message' => 'Template name is required.' ), 400 );
    }

    // Add prefix based on type
    $prefix = '';
    if ( $type === 'order' ) {
        $prefix = 'order:';
    } elseif ( $type === 'product' ) {
        $prefix = 'product:';
    } elseif ( $type === 'customer' ) {
        $prefix = 'customer:';
    } elseif ( $type === 'coupon' ) {
        $prefix = 'coupon:';
    } elseif ( $type === 'category' || $type === 'categories' ) {
        $prefix = 'category:';
    } elseif ( $type === 'tag' || $type === 'tags' ) {
        $prefix = 'tag:';
    } elseif ( $type === 'attribute' || $type === 'attributes' ) {
        $prefix = 'attribute:';
    } elseif ( $type === 'variation' || $type === 'variations' ) {
        $prefix = 'variation:';
    } elseif ( $type === 'review' || $type === 'reviews' ) {
        $prefix = 'review:';
    } elseif ( $type === 'download' || $type === 'downloads' ) {
        $prefix = 'download:';
    }

    // If type is still empty (couldn't be detected on JS side), default templates to "product"
    if ( $type === '' ) {
        $type   = 'product';
        $prefix = 'product:';
    }

    // Only add prefix if name doesn't already have one
    if ( $prefix && strpos( $name, $prefix ) !== 0 ) {
        // Check if it has a different prefix and remove it
        $name = preg_replace( '/^(order|product|customer|coupon|category|tag|attribute|variation|review|download):/', '', $name );
        $name = $prefix . $name;
    }

    $templates = get_option( self::get_templates_option_key(), array() );
    if ( ! is_array( $templates ) ) {
        $templates = array();
    }

    $templates[ $name ] = array(
        'updated_at' => current_time( 'mysql' ),
        'data'       => $data,
        'type'       => $type, // Store type for future reference
    );

    update_option( self::get_templates_option_key(), $templates, false );

    return array(
        'saved' => true,
        'name'  => $name,
    );
}

private static function get_filter_presets_option_key() {
    return 'mt_primelink_filter_presets';
}

public static function list_filter_presets() {
    $presets = get_option( self::get_filter_presets_option_key(), array() );
    if ( ! is_array( $presets ) ) {
        $presets = array();
    }
    $names = array_keys( $presets );
    sort( $names );
    return array( 'presets' => $names );
}

public static function save_filter_preset( $request ) {
    $name    = sanitize_text_field( $request['name'] ?? '' );
    $filters = isset( $request['filters'] ) ? (array) $request['filters'] : array();

    if ( $name === '' ) {
        return new \WP_REST_Response( array( 'message' => 'Preset name is required.' ), 400 );
    }

    $filters = self::normalize_filters( $filters );

    $presets = get_option( self::get_filter_presets_option_key(), array() );
    if ( ! is_array( $presets ) ) {
        $presets = array();
    }

    $presets[ $name ] = array(
        'updated_at' => current_time( 'mysql' ),
        'filters'    => $filters,
    );

    update_option( self::get_filter_presets_option_key(), $presets, false );

    return array( 'saved' => true, 'name' => $name );
}

public static function load_filter_preset( $request ) {
    $name = sanitize_text_field( $request['name'] ?? '' );
    if ( $name === '' ) {
        return new \WP_REST_Response( array( 'message' => 'Preset name is required.' ), 400 );
    }

    $presets = get_option( self::get_filter_presets_option_key(), array() );
    if ( empty( $presets[ $name ] ) ) {
        return new \WP_REST_Response( array( 'message' => 'Preset not found.' ), 404 );
    }

    return array(
        'name'    => $name,
        'filters' => $presets[ $name ]['filters'] ?? array(),
    );
}

public static function delete_filter_preset( $request ) {
    $name = sanitize_text_field( $request['name'] ?? '' );
    if ( $name === '' ) {
        return new \WP_REST_Response( array( 'message' => 'Preset name is required.' ), 400 );
    }

    $presets = get_option( self::get_filter_presets_option_key(), array() );
    if ( isset( $presets[ $name ] ) ) {
        unset( $presets[ $name ] );
        update_option( self::get_filter_presets_option_key(), $presets, false );
    }

    return array( 'deleted' => true, 'name' => $name );
}

public static function load_template( $request ) {
    $name = sanitize_text_field( $request['name'] ?? '' );
    $import_id = absint( $request['import_id'] ?? 0 );

    // If import_id is provided, load from import draft instead of template
    if ( $import_id > 0 ) {
        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        if ( empty( $drafts[ $import_id ] ) ) {
            return new \WP_REST_Response( array( 'message' => 'Import not found.' ), 404 );
        }

        $draft = $drafts[ $import_id ];
        
        // Extract mapping data from step 5
        $mapping_data = $draft['step_data'][5] ?? array();
        
        return array(
            'import_id' => $import_id,
            'name'      => sprintf( 'Import #%d', $import_id ),
            'data'      => $mapping_data,
            'is_import' => true,
        );
    }

    // Otherwise, load from template
    $templates = get_option( self::get_templates_option_key(), array() );
    if ( empty( $templates[ $name ]['data'] ) ) {
        return new \WP_REST_Response( array( 'message' => 'Template not found.' ), 404 );
    }

    $data = $templates[ $name ]['data'];

    return array(
        'name'      => $name,
        'data'      => $data,
        'is_import' => false,
    );
}

public static function delete_template( $request ) {
    $name = sanitize_text_field( $request['name'] );

    $templates = get_option( self::get_templates_option_key(), array() );
    if ( ! is_array( $templates ) ) {
        $templates = array();
    }

    if ( isset( $templates[ $name ] ) ) {
        unset( $templates[ $name ] );
        update_option( self::get_templates_option_key(), $templates, false );
    }

    return array(
        'deleted' => true,
        'name'    => $name,
    );
}

public static function get_draft( $request ) {
    $import_id = absint( $request['import_id'] );

    $drafts = get_option( 'mt_primelink_import_drafts', array() );
    if ( empty( $drafts[ $import_id ] ) ) {
        return new \WP_REST_Response( array( 'message' => 'Draft not found.' ), 404 );
    }

    return array(
        'import_id' => $import_id,
        'draft'     => $drafts[ $import_id ],
    );
}

/**
 * List all imports for template/import selection dropdown.
 * 
 * @param \WP_REST_Request $request Request object.
 * @return array List of imports with ID, type, and status.
 */
public static function list_imports( $request ) {
    $type_filter = sanitize_text_field( $request['type'] ?? '' );
    
    $drafts = get_option( 'mt_primelink_import_drafts', array() );
    if ( ! is_array( $drafts ) ) {
        $drafts = array();
    }

    $imports = array();
    foreach ( $drafts as $import_id => $draft ) {
        $import_type = $draft['type'] ?? ( $draft['step_data'][1]['type'] ?? 'product' );
        
        // Normalize type
        if ( $import_type === 'products' ) {
            $import_type = 'product';
        } elseif ( $import_type === 'orders' ) {
            $import_type = 'order';
        } elseif ( $import_type === 'customers' ) {
            $import_type = 'customer';
        }
        
        // Filter by type if specified
        if ( ! empty( $type_filter ) && $import_type !== $type_filter ) {
            continue;
        }
        
        // Get status
        $state = get_option( 'mt_primelink_import_state_' . absint( $import_id ), array() );
        $status = $state['status'] ?? 'idle';
        
        // Get step
        $current_step = $draft['current_step'] ?? 1;
        
        // Get created date
        $created = $draft['created_at'] ?? '';
        
        $imports[] = array(
            'id'           => absint( $import_id ),
            'type'         => $import_type,
            'status'       => $status,
            'step'         => $current_step,
            'created'      => $created,
            'display_name' => sprintf( 
                'Import #%d (%s) - %s', 
                absint( $import_id ),
                ucfirst( $import_type ),
                ucfirst( $status )
            ),
        );
    }
    
    // Sort by created date (newest first)
    usort( $imports, function( $a, $b ) {
        $at = strtotime( $a['created'] ?? '' );
        $bt = strtotime( $b['created'] ?? '' );
        return $bt <=> $at;
    } );

    return array(
        'imports' => $imports,
    );
}

/**
 * Reload (duplicate) an import with the same settings.
 * Creates a new import draft with all the same step data.
 * 
 * @param \WP_REST_Request $request Request object.
 * @return array New import ID.
 */
public static function reload_import( $request ) {
    $source_import_id = absint( $request['import_id'] ?? 0 );
    
    if ( $source_import_id < 1 ) {
        return new \WP_REST_Response( array( 'message' => 'Invalid import ID.' ), 400 );
    }
    
    $drafts = get_option( 'mt_primelink_import_drafts', array() );
    if ( empty( $drafts[ $source_import_id ] ) ) {
        return new \WP_REST_Response( array( 'message' => 'Source import not found.' ), 404 );
    }
    
    $source_draft = $drafts[ $source_import_id ];
    
    // Create new draft with same data
    $new_id = time();
    $new_draft = array(
        'created_at'    => current_time( 'mysql' ),
        'step_data'     => $source_draft['step_data'] ?? array(),
        'current_step'  => $source_draft['current_step'] ?? 1,
        'type'          => $source_draft['type'] ?? '',
    );
    
    $drafts[ $new_id ] = $new_draft;
    update_option( 'mt_primelink_import_drafts', $drafts, false );
    
    return array(
        'import_id' => $new_id,
        'message'   => sprintf( 'Import #%d reloaded as Import #%d', $source_import_id, $new_id ),
    );
}

    public static function notify_test( $request ) {
        $email = sanitize_email( $request['email'] ?? '' );
        if ( ! $email || ! is_email( $email ) ) {
            return new \WP_REST_Response( array( 'message' => 'Invalid email.' ), 400 );
        }
        $sent = wp_mail( $email, 'PrimeLink test notification', 'This is a test email from PrimeLink notifications.' );
        if ( ! $sent ) {
            return new \WP_REST_Response( array( 'message' => 'Failed to send email.' ), 500 );
        }
        return array( 'message' => 'Test email sent to ' . $email );
    }

    /**
     * Test and activate license from Settings page.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|array
     */
    public static function test_license( $request ) {
        $purchase_code = sanitize_text_field( $request['purchase_code'] ?? '' );
        $client_name   = sanitize_text_field( $request['client_name'] ?? '' );

        if ( empty( $purchase_code ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'Purchase code is required.', 'primelink' ),
            ), 400 );
        }

        if ( empty( $client_name ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'Client name (Envato username) is required.', 'primelink' ),
            ), 400 );
        }

        // Activate license via Envato API
        $activation = \PrimeLink\Helpers\EnvatoHelper::activate_license( $purchase_code, $client_name, true );

        if ( ! isset( $activation['status'] ) || $activation['status'] !== true ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => isset( $activation['message'] ) ? $activation['message'] : __( 'License activation failed.', 'primelink' ),
            ), 400 );
        }

        // Save license key in settings
        $settings = self::get_settings();
        $settings['license_key'] = $purchase_code;
        $settings['license_client_name'] = $client_name;
        $settings['license_status'] = 'active';
        update_option( self::get_settings_option_key(), $settings, false );

        return array(
            'success' => true,
            'message' => isset( $activation['message'] ) ? $activation['message'] : __( 'License activated successfully!', 'primelink' ),
            'data'    => array(
                'license_code' => $purchase_code,
                'client_name'  => $client_name,
            ),
        );
    }

    /**
     * Deactivate license from Settings page.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|array
     */
    public static function deactivate_license( $request ) {
        $activation = get_option( 'mt_primelink_license_activation', array() );
        
        if ( empty( $activation ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'No active license found.', 'primelink' ),
            ), 400 );
        }

        $license_code = $activation['license_code'] ?? '';
        $client_name  = $activation['client_name'] ?? '';

        // Deactivate license via Envato API
        $deactivation = \PrimeLink\Helpers\EnvatoHelper::deactivate_license( $license_code, $client_name );

        if ( ! isset( $deactivation['status'] ) || $deactivation['status'] !== true ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => isset( $deactivation['message'] ) ? $deactivation['message'] : __( 'License deactivation failed.', 'primelink' ),
            ), 400 );
        }

        // Clear license from settings
        $settings = self::get_settings();
        $settings['license_key'] = '';
        $settings['license_client_name'] = '';
        $settings['license_status'] = 'inactive';
        update_option( self::get_settings_option_key(), $settings, false );

        return array(
            'success' => true,
            'message' => isset( $deactivation['message'] ) ? $deactivation['message'] : __( 'License deactivated successfully.', 'primelink' ),
        );
    }

    /**
     * Check for updates (for debugging/testing).
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|array
     */
    public static function check_updates( $request ) {
        // Clear cache to force fresh check
        delete_transient( 'mt_primelink_next_update_check' );
        
        // Clear WordPress update cache to force refresh
        delete_site_transient( 'update_plugins' );
        
        $license_status = \PrimeLink\Helpers\EnvatoHelper::get_license_status();
        
        if ( ! $license_status['activated'] ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'License is not activated. Please activate your license first.', 'primelink' ),
                'license_status' => $license_status,
            ), 400 );
        }

        $current_version = \PrimeLink\Helpers\EnvatoHelper::get_current_version();
        
        // Force fresh check (clears all caches)
        $update_data = \PrimeLink\Core\Updater::check_for_updates();

        return array(
            'success' => true,
            'current_version' => $current_version,
            'update_data' => $update_data,
            'license_status' => $license_status,
            'message' => __( 'For updates, please check CodeCanyon or download manually.', 'primelink' ),
        );
    }

    /** ---------------- Activation Wizard ---------------- */

    /**
     * Verify license in activation wizard.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|array
     */
    public static function wizard_verify_license( $request ) {
        $purchase_code = sanitize_text_field( $request['purchase_code'] ?? '' );
        $client_name   = sanitize_text_field( $request['client_name'] ?? '' );

        if ( empty( $purchase_code ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'Purchase code is required.', 'primelink' ),
            ), 400 );
        }

        if ( empty( $client_name ) ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => __( 'Client name (Envato username) is required.', 'primelink' ),
            ), 400 );
        }

        // Activate license via Envato API
        $activation = \PrimeLink\Helpers\EnvatoHelper::activate_license( $purchase_code, $client_name, true );

        if ( ! isset( $activation['status'] ) || $activation['status'] !== true ) {
            return new \WP_REST_Response( array(
                'success' => false,
                'error'   => isset( $activation['message'] ) ? $activation['message'] : __( 'License activation failed.', 'primelink' ),
            ), 400 );
        }

        // Save license key in settings
        $settings = self::get_settings();
        $settings['license_key'] = $purchase_code;
        $settings['license_client_name'] = $client_name;
        $settings['license_status'] = 'active';
        update_option( self::get_settings_option_key(), $settings, false );

        return array(
            'success' => true,
            'message' => isset( $activation['message'] ) ? $activation['message'] : __( 'License activated successfully!', 'primelink' ),
            'data'    => array(
                'license_code' => $purchase_code,
                'client_name'  => $client_name,
            ),
        );
    }

    /**
     * Save setup data from activation wizard.
     *
     * @param WP_REST_Request $request Request object.
     * @return array
     */
    public static function wizard_save_setup( $request ) {
        $notify_email = sanitize_email( $request['notify_email'] ?? '' );
        $iteration_size = isset( $request['iteration_size'] ) ? max( 1, min( 500, absint( $request['iteration_size'] ) ) ) : 20;
        $enable_logs = ! empty( $request['enable_logs'] ) ? 1 : 0;

        $settings = self::get_settings();
        
        if ( ! empty( $notify_email ) ) {
            $settings['notify_email'] = $notify_email;
        }
        
        $settings['default_iteration_size'] = $iteration_size;
        $settings['enable_logs'] = $enable_logs;

        update_option( self::get_settings_option_key(), $settings, false );

        return array(
            'success' => true,
            'message' => __( 'Settings saved successfully.', 'primelink' ),
        );
    }

    /**
     * Test system requirements in activation wizard.
     *
     * @param WP_REST_Request $request Request object.
     * @return array
     */
    public static function wizard_test_system( $request ) {
        $tests = array();
        $all_passed = true;

        // Check PHP version
        $php_version = PHP_VERSION;
        $php_required = '7.4';
        $php_ok = version_compare( $php_version, $php_required, '>=' );
        $tests[] = array(
            'name'    => __( 'PHP Version', 'primelink' ),
            'status'  => $php_ok ? 'pass' : 'fail',
            'message' => $php_ok 
                ? sprintf( __( 'PHP %s (Required: %s+)', 'primelink' ), $php_version, $php_required )
                : sprintf( __( 'PHP %s (Required: %s+)', 'primelink' ), $php_version, $php_required ),
        );
        if ( ! $php_ok ) {
            $all_passed = false;
        }

        // Check WordPress version
        global $wp_version;
        $wp_required = '5.8';
        $wp_ok = version_compare( $wp_version, $wp_required, '>=' );
        $tests[] = array(
            'name'    => __( 'WooCommerce Version', 'primelink' ),
            'status'  => $wp_ok ? 'pass' : 'fail',
            'message' => $wp_ok 
                ? sprintf( __( 'WooCommerce %s (Required: %s+)', 'primelink' ), $wp_version, $wp_required )
                : sprintf( __( 'WooCommerce %s (Required: %s+)', 'primelink' ), $wp_version, $wp_required ),
        );
        if ( ! $wp_ok ) {
            $all_passed = false;
        }

        // Check memory limit
        $memory_limit = ini_get( 'memory_limit' );
        $memory_bytes = wp_convert_hr_to_bytes( $memory_limit );
        $memory_required = 128 * 1024 * 1024; // 128MB
        $memory_ok = $memory_bytes >= $memory_required;
        $tests[] = array(
            'name'    => __( 'Memory Limit', 'primelink' ),
            'status'  => $memory_ok ? 'pass' : 'warning',
            'message' => sprintf( __( '%s (Recommended: 128MB+)', 'primelink' ), $memory_limit ),
        );

        // Check upload directory permissions
        $upload_dir = wp_upload_dir();
        $upload_ok = wp_is_writable( $upload_dir['basedir'] );
        $tests[] = array(
            'name'    => __( 'Upload Directory', 'primelink' ),
            'status'  => $upload_ok ? 'pass' : 'fail',
            'message' => $upload_ok 
                ? __( 'Writable', 'primelink' )
                : __( 'Not writable', 'primelink' ),
        );
        if ( ! $upload_ok ) {
            $all_passed = false;
        }

        // Check required PHP extensions
        $extensions = array( 'curl', 'json', 'mbstring', 'zip' );
        foreach ( $extensions as $ext ) {
            $ext_ok = extension_loaded( $ext );
            $tests[] = array(
                'name'    => sprintf( __( 'PHP Extension: %s', 'primelink' ), $ext ),
                'status'  => $ext_ok ? 'pass' : 'fail',
                'message' => $ext_ok ? __( 'Loaded', 'primelink' ) : __( 'Not loaded', 'primelink' ),
            );
            if ( ! $ext_ok ) {
                $all_passed = false;
            }
        }

        return array(
            'success'    => $all_passed,
            'tests'      => $tests,
            'all_passed' => $all_passed,
        );
    }

    /**
     * Complete activation wizard.
     *
     * @param WP_REST_Request $request Request object.
     * @return array
     */
    public static function wizard_complete( $request ) {
        \PrimeLink\Core\Admin\ActivationWizard::complete_wizard();

        return array(
            'success' => true,
            'message' => __( 'Wizard completed successfully!', 'primelink' ),
        );
    }

    /**
     * Restart activation wizard.
     *
     * @param WP_REST_Request $request Request object.
     * @return array
     */
    public static function wizard_restart( $request ) {
        // Reset wizard flags - clear skip flag and show wizard
        update_option( 'mt_primelink_show_activation_wizard', true, false );
        delete_option( 'mt_primelink_wizard_completed' );
        delete_option( 'mt_primelink_wizard_skipped' );

        return array(
            'success' => true,
            'message' => __( 'Wizard restarted. Please refresh the page.', 'primelink' ),
        );
    }

    /**
     * Skip activation wizard (user chose to skip for now).
     *
     * @param WP_REST_Request $request Request object.
     * @return array
     */
    public static function wizard_skip( $request ) {
        // Set skip flag - wizard won't show again until restart
        update_option( 'mt_primelink_wizard_skipped', true, false );
        delete_option( 'mt_primelink_show_activation_wizard' );

        return array(
            'success' => true,
            'message' => __( 'Wizard skipped. You can restart it anytime from the dashboard.', 'primelink' ),
        );
    }

    /**
     * Get system status (detailed).
     * Developer API endpoint.
     *
     * @param \WP_REST_Request $request Request object.
     * @return array
     */
    public static function system_status( $request ) {
        global $wp_version, $wpdb;

        $status = array(
            'plugin' => array(
                'name'    => 'PrimeLink',
                'version' => MT_PRIMELINK_VERSION,
                'active'  => true,
            ),
            'wordpress' => array(
                'version' => $wp_version,
                'multisite' => is_multisite(),
            ),
            'php' => array(
                'version'      => PHP_VERSION,
                'memory_limit' => ini_get( 'memory_limit' ),
                'max_execution_time' => ini_get( 'max_execution_time' ),
                'upload_max_filesize' => ini_get( 'upload_max_filesize' ),
                'post_max_size' => ini_get( 'post_max_size' ),
            ),
            'database' => array(
                'version' => $wpdb->db_version(),
                'charset' => $wpdb->charset,
                'collate' => $wpdb->collate,
            ),
            'server' => array(
                'software' => isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : 'Unknown',
            ),
            'extensions' => array(
                'curl'     => extension_loaded( 'curl' ),
                'json'     => extension_loaded( 'json' ),
                'mbstring' => extension_loaded( 'mbstring' ),
                'zip'      => extension_loaded( 'zip' ),
                'xml'      => extension_loaded( 'xml' ),
                'libxml'   => extension_loaded( 'libxml' ),
                'simplexml' => function_exists( 'simplexml_load_file' ),
                'dom'      => class_exists( 'DOMDocument' ),
            ),
            'woocommerce' => array(
                'active'  => class_exists( 'WooCommerce' ),
                'version' => class_exists( 'WooCommerce' ) ? WC()->version : null,
            ),
            'uploads' => array(
                'writable' => wp_is_writable( wp_upload_dir()['basedir'] ),
                'path'     => wp_upload_dir()['basedir'],
            ),
            'settings' => array(
                'cron_mode' => get_option( 'mt_primelink_settings', array() )['cron_mode'] ?? 'wp',
                'rate_limit_max' => get_option( 'mt_primelink_settings', array() )['rate_limit_max'] ?? 500,
            ),
            'statistics' => array(
                'total_imports' => count( get_option( 'mt_primelink_import_drafts', array() ) ),
                'total_exports' => count( get_option( 'mt_primelink_export_drafts', array() ) ),
                'active_imports' => self::count_active_imports(),
            ),
        );

        return $status;
    }

    /**
     * Health check endpoint (public).
     * Returns basic health status without sensitive information.
     *
     * @param \WP_REST_Request $request Request object.
     * @return array
     */
    public static function health_check( $request ) {
        $health = array(
            'status' => 'healthy',
            'timestamp' => current_time( 'mysql' ),
            'plugin' => array(
                'active' => true,
                'version' => MT_PRIMELINK_VERSION,
            ),
            'checks' => array(
                'php_version' => version_compare( PHP_VERSION, '7.4', '>=' ),
                'wordpress' => true,
                'uploads_writable' => wp_is_writable( wp_upload_dir()['basedir'] ),
                'woocommerce' => class_exists( 'WooCommerce' ),
            ),
        );

        // Determine overall health
        $all_checks_pass = true;
        foreach ( $health['checks'] as $check ) {
            if ( ! $check ) {
                $all_checks_pass = false;
                break;
            }
        }

        if ( ! $all_checks_pass ) {
            $health['status'] = 'degraded';
        }

        return $health;
    }

    /**
     * Get system information (developer-friendly).
     *
     * @param \WP_REST_Request $request Request object.
     * @return array
     */
    public static function system_info( $request ) {
        return array(
            'api' => array(
                'version' => '1.0',
                'namespace' => 'primelink/v1',
                'base_url' => rest_url( 'primelink/v1/' ),
            ),
            'plugin' => array(
                'name' => 'PrimeLink',
                'version' => MT_PRIMELINK_VERSION,
                'path' => MT_PRIMELINK_PATH,
                'url' => MT_PRIMELINK_URL,
            ),
            'capabilities' => array(
                'required' => apply_filters( 'mt_primelink_capability', class_exists( 'WooCommerce' ) ? 'manage_woocommerce' : 'manage_options' ),
                'current_user_can' => current_user_can( apply_filters( 'mt_primelink_capability', class_exists( 'WooCommerce' ) ? 'manage_woocommerce' : 'manage_options' ) ),
            ),
            'filters' => array(
                'mt_primelink_capability' => 'Filter to customize required capability',
                'mt_primelink_allow_webhook_without_token' => 'Filter to allow webhooks without token',
            ),
            'endpoints' => array(
                'import' => array(
                    'count' => 30,
                    'base' => '/import',
                ),
                'export' => array(
                    'count' => 12,
                    'base' => '/export',
                ),
                'live_feed' => array(
                    'count' => 8,
                    'base' => '/live-feed',
                ),
            ),
        );
    }

    /**
     * Count active (running) imports.
     *
     * @return int
     */
    private static function count_active_imports() {
        $drafts = get_option( 'mt_primelink_import_drafts', array() );
        $active = 0;

        foreach ( $drafts as $import_id => $draft ) {
            $state = get_option( 'mt_primelink_import_state_' . absint( $import_id ), array() );
            if ( isset( $state['status'] ) && $state['status'] === 'running' ) {
                $active++;
            }
        }

        return $active;
    }

}
