<?php

declare(strict_types=1);

namespace BadamSoft\WooProductExporter\Admin;

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

use BadamSoft\WooProductExporter\Access\AccessManager;
use BadamSoft\WooProductExporter\Core\Plugin;
use BadamSoft\WooProductExporter\Exporter\Exporter;
use BadamSoft\WooProductExporter\Filters\FilterManager;
use BadamSoft\WooProductExporter\Helpers\DownloadHelper;
use BadamSoft\WooProductExporter\Helpers\RunLogFormatter;
use BadamSoft\WooProductExporter\Helpers\Utils;
use BadamSoft\WooProductExporter\Scheduler\ScheduledExportManager;
use BadamSoft\WooProductExporter\Templates\TemplateController;
use BadamSoft\WooProductExporter\Templates\TemplateManager;
use BadamSoft\WooProductExporter\Settings\SettingsRepository;
use DateTimeImmutable;
use DateTimeZone;
use Throwable;
use function home_url;

class AdminPage {
    private Plugin $plugin;
    private Exporter $exporter;
    private TemplateManager $template_manager;
    private ScheduledExportManager $scheduled_manager;
    private AccessManager $access_manager;
    private FilterManager $filter_manager;
    private SettingsRepository $settings_repository;
    /**
     * @var array<string, bool>|null
     */
    private ?array $capability_flags = null;
    private ?array $asset_manifest = null;
    private bool $late_external_dequeue_registered = false;

    private const MENU_SLUG            = 'badamsoft-product-exporter-for-woocommerce';
    private const MENU_HISTORY_SLUG    = 'badamsoft-product-exporter-for-woocommerce-history';
    private const MANUAL_EXPORT_DIRECT_THRESHOLD = 500;

    /**
     * AdminPage constructor.
     *
     * Wires core services and registers base hooks for scripts and AJAX actions.
     *
     * @param Plugin               $plugin                Core plugin instance.
     * @param Exporter             $exporter              Exporter service.
     * @param TemplateManager      $template_manager      Template manager.
     * @param ScheduledExportManager $scheduled_manager   Scheduled export manager.
     * @param AccessManager        $access_manager        Access control manager.
     * @param FilterManager        $filter_manager        Filter manager.
     * @param SettingsRepository   $settings_repository   Settings repository.
     */
    public function __construct( Plugin $plugin, Exporter $exporter, TemplateManager $template_manager, ScheduledExportManager $scheduled_manager, AccessManager $access_manager, FilterManager $filter_manager, SettingsRepository $settings_repository ) {
        $this->plugin             = $plugin;
        $this->exporter           = $exporter;
        $this->template_manager   = $template_manager;
        $this->scheduled_manager  = $scheduled_manager;
        $this->access_manager     = $access_manager;
        $this->filter_manager     = $filter_manager;
        $this->settings_repository    = $settings_repository;

        add_filter( 'script_loader_tag', [ $this, 'ensure_app_script_module_type' ], PHP_INT_MAX, 3 );
        add_action( 'wp_ajax_prodexfo_manual_export_download', [ $this, 'handle_manual_export_download' ] );
        add_action( 'wp_ajax_prodexfo_manual_export_start', [ $this, 'handle_manual_export_start' ] );
        add_action( 'wp_ajax_prodexfo_manual_export_status', [ $this, 'handle_manual_export_status' ] );
        add_action( 'wp_ajax_prodexfo_manual_export_direct', [ $this, 'handle_manual_export_direct' ] );
    }

    /**
     * Ensure the admin app script keeps type="module" and preserves extra attributes.
     *
     * @param string $tag    Original script HTML tag.
     * @param string $handle Script handle.
     * @param string $src    Script source URL.
     * @return string
     */
    public function ensure_app_script_module_type( string $tag, string $handle, string $src ): string {
        if ( 'prodexfo-admin-app' !== $handle ) {
            return $tag;
        }

        if ( empty( $src ) ) {
            return $tag;
        }

        // Ensure the script tag uses module type even if other hooks strip it.
        $attributes = [
            'type' => 'module',
            'src'  => $src,
            'id'   => $handle . '-js',
        ];

        // Preserve additional attributes (e.g. defer) from original tag if present.
        if ( preg_match_all( '/\s([a-zA-Z-]+)=("|")(.*?)(\2)/', $tag, $matches, PREG_SET_ORDER ) ) {
            foreach ( $matches as $match ) {
                $attr = strtolower( $match[1] );

                if ( in_array( $attr, [ 'src', 'type', 'id' ], true ) ) {
                    continue;
                }

                $attributes[ $attr ] = html_entity_decode( $match[3], ENT_QUOTES );
            }
        }

        $compiled = '<script';

        foreach ( $attributes as $name => $value ) {
            if ( is_bool( $value ) ) {
                if ( $value ) {
                    $compiled .= ' ' . $name;
                }

                continue;
            }

            $compiled .= sprintf( ' %s="%s"', $name, esc_attr( (string) $value ) );
        }

        $compiled .= '></script>';

        return $compiled;
    }

    /**
     * Render the Export History admin page.
     */
    public function render_history_page(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to view export history.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        settings_errors( 'prodexfo_history' );

        include $this->plugin->get_view_path( 'react-app.php' );
    }

    private function sanitize_time_value( $value ): string {
        $value = is_string( $value ) ? trim( $value ) : '';

        if ( ! preg_match( '/^\d{1,2}:\d{2}$/', $value ) ) {
            return '';
        }

        return $value;
    }

    private function is_valid_time( $value ): bool {
        if ( ! is_string( $value ) || '' === $value ) {
            return false;
        }

        return (bool) preg_match( '/^([01]?\d|2[0-3]):[0-5]\d$/', $value );
    }

    private function sanitize_timezone_value( string $value ): string {
        $value = trim( $value );

        if ( '' === $value ) {
            return Utils::get_timezone_for_dropdown();
        }

        return Utils::create_timezone( $value )->getName();
    }

    /**
     * @param array<int|string, mixed> $actions
     * @return array<int, array<string, mixed>>
     */
    private function sanitize_actions_payload( array $actions ): array {
        return [];
    }

    private function get_manual_export_threshold(): int {
        $threshold = (int) apply_filters( 'prodexfo_manual_export_direct_threshold', self::MANUAL_EXPORT_DIRECT_THRESHOLD );

        return max( 1, $threshold );
    }

    /**
     * AJAX: Start manual export using a safer endpoint than admin.php POST.
     */
    public function handle_manual_export_start(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to export products.', 'badamsoft-product-exporter-for-woocommerce' ) ], 403 );
        }

        check_ajax_referer( 'prodexfo_manual_export_action', 'nonce' );

        $raw_post = filter_input_array( INPUT_POST, FILTER_UNSAFE_RAW );
        $request  = is_array( $raw_post ) ? wp_unslash( $raw_post ) : [];
        $fields  = $this->exporter->sanitize_fields_from_request( $request );

        if ( empty( $fields ) ) {
            wp_send_json_error( [ 'message' => esc_html__( 'Select at least one field before exporting.', 'badamsoft-product-exporter-for-woocommerce' ) ], 400 );
        }

        $this->exporter->remember_selected_fields( $fields );
        $filters  = $this->exporter->sanitize_filters_from_request( $request );
        $format   = $this->exporter->sanitize_format_from_request( $request );
        $settings = $this->exporter->sanitize_settings_from_request( $request );
        $this->exporter->remember_filters( $filters );
        $this->exporter->remember_settings( $settings );

        $run_id = $this->scheduled_manager->record_run(
            [
                'run_type'      => 'manual',
                'status'        => 'pending',
                'rows_exported' => 0,
                'file_path'     => null,
                'file_size'     => 0,
                'fields_json'   => wp_json_encode( array_values( $fields ) ),
                'filters_json'  => wp_json_encode( $filters ),
                'settings_json' => wp_json_encode( $settings ),
                'file_format'   => strtoupper( sanitize_key( $format ) ),
                'log'           => RunLogFormatter::formatSuccessLog(
                    [
                        'rows'        => 0,
                        'file_format' => strtoupper( sanitize_key( $format ) ),
                    ]
                ),
            ]
        );

        if ( $run_id <= 0 ) {
            wp_send_json_error( [ 'message' => esc_html__( 'Failed to start export.', 'badamsoft-product-exporter-for-woocommerce' ) ], 500 );
        }

        $scheduled = false;

        if ( function_exists( 'as_schedule_single_action' ) ) {
            $action_id = as_schedule_single_action( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } elseif ( function_exists( 'as_enqueue_async_action' ) ) {
            $action_id = as_enqueue_async_action( Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } else {
            $scheduled = wp_schedule_single_event( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
        }

        if ( ! $scheduled ) {
            $this->scheduled_manager->update_run_file_meta(
                $run_id,
                [
                    'status'      => 'error',
                    'finished_at' => current_time( 'mysql' ),
                    'log'         => RunLogFormatter::formatFailureLog(
                        [
                            'error' => 'Unable to schedule export run.',
                        ]
                    ),
                ]
            );
            wp_send_json_error( [ 'message' => esc_html__( 'Unable to schedule export run.', 'badamsoft-product-exporter-for-woocommerce' ) ], 500 );
        }

        if ( class_exists( '\\ActionScheduler_QueueRunner' ) ) {
            try {
                \ActionScheduler_QueueRunner::instance()->maybe_dispatch_async_request();
            } catch ( \Throwable $e ) {
                // Intentionally ignored.
            }
        } elseif ( function_exists( 'spawn_cron' ) ) {
            spawn_cron();
        }

        wp_send_json_success(
            [
                'mode'         => 'async',
                'run_id'       => $run_id,
                'notice_nonce' => wp_create_nonce( 'prodexfo_async_export_notice' ),
            ]
        );
    }

    /**
     * AJAX: Return current status for a manual export run.
     */
    public function handle_manual_export_status(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => esc_html__( 'You do not have permission to view export status.', 'badamsoft-product-exporter-for-woocommerce' ) ], 403 );
        }

        check_ajax_referer( 'prodexfo_manual_export_status', 'nonce' );

        $run_id = absint( filter_input( INPUT_POST, 'run_id', FILTER_UNSAFE_RAW ) );

        if ( $run_id <= 0 ) {
            wp_send_json_error( [ 'message' => esc_html__( 'Invalid run ID.', 'badamsoft-product-exporter-for-woocommerce' ) ], 400 );
        }

        $run = $this->scheduled_manager->get_run( $run_id );

        if ( ! $run ) {
            wp_send_json_error( [ 'message' => esc_html__( 'Export run not found.', 'badamsoft-product-exporter-for-woocommerce' ) ], 404 );
        }

        if ( isset( $run['status'] ) && is_string( $run['status'] ) ) {
            $status = sanitize_key( $run['status'] );

            if ( in_array( $status, [ 'pending', 'running' ], true ) ) {
                if ( class_exists( '\\ActionScheduler_QueueRunner' ) ) {
                    try {
                        \ActionScheduler_QueueRunner::instance()->maybe_dispatch_async_request();
                    } catch ( \Throwable $e ) {
                        // Intentionally ignored.
                    }
                } elseif ( function_exists( 'spawn_cron' ) ) {
                    spawn_cron();
                }
            }

            if ( 'pending' === $status ) {
                $this->maybe_reschedule_pending_manual_export( $run, $run_id );
                $this->maybe_work_pending_manual_export( $run, $run_id );
            }
        }

        wp_send_json_success( [ 'run' => $run ] );
    }
    private function get_run_created_timestamp( string $created_at ): ?int {
        $created_at = trim( $created_at );

        if ( '' === $created_at ) {
            return null;
        }

        try {
            $date = new DateTimeImmutable( $created_at, wp_timezone() );
        } catch ( Throwable $e ) {
            return null;
        }

        return (int) $date->format( 'U' );
    }
    private function current_utc_timestamp(): int {
        return (int) current_time( 'timestamp', true );
    }
    private function maybe_work_pending_manual_export( array $run, int $run_id ): void {
        if ( isset( $run['run_type'] ) && 'manual' !== (string) $run['run_type'] ) {
            return;
        }

        if ( isset( $run['status'] ) && 'pending' !== (string) $run['status'] ) {
            return;
        }

        $created_at = isset( $run['created_at'] ) ? (string) $run['created_at'] : '';

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

        $created_ts = $this->get_run_created_timestamp( $created_at );

        if ( ! $created_ts || ( $this->current_utc_timestamp() - $created_ts ) < 20 ) {
            return;
        }

        $throttle_key = 'prodexfo_manual_export_worker_throttle_' . $run_id;

        if ( false !== get_transient( $throttle_key ) ) {
            return;
        }

        $lock_key = 'prodexfo_manual_export_worker_lock_' . $run_id;

        if ( false !== get_transient( $lock_key ) ) {
            return;
        }

        set_transient( $lock_key, time(), 600 );
        set_transient( $throttle_key, time(), 60 );

        try {
            do_action( Plugin::MANUAL_EXPORT_ASYNC_HOOK, $run_id ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
        } catch ( Throwable $e ) {
            // Intentionally ignored.
        } finally {
            delete_transient( $lock_key );
        }
    }
    private function maybe_reschedule_pending_manual_export( array $run, int $run_id ): void {
        if ( isset( $run['run_type'] ) && 'manual' !== (string) $run['run_type'] ) {
            return;
        }

        $created_at = isset( $run['created_at'] ) ? (string) $run['created_at'] : '';

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

        $created_ts = $this->get_run_created_timestamp( $created_at );

        if ( ! $created_ts || ( $this->current_utc_timestamp() - $created_ts ) < 20 ) {
            return;
        }

        $kick_key = 'prodexfo_manual_export_kick_' . $run_id;

        if ( false !== get_transient( $kick_key ) ) {
            return;
        }

        set_transient( $kick_key, time(), 120 );

        if ( function_exists( 'as_unschedule_all_actions' ) ) {
            try {
                as_unschedule_all_actions( Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            } catch ( Throwable $e ) {
                // Intentionally ignored.
            }
        }

        $scheduled = false;

        if ( function_exists( 'as_schedule_single_action' ) ) {
            $action_id = as_schedule_single_action( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } elseif ( function_exists( 'as_enqueue_async_action' ) ) {
            $action_id = as_enqueue_async_action( Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } elseif ( function_exists( 'wp_schedule_single_event' ) ) {
            $scheduled = wp_schedule_single_event( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
        }

        if ( ! $scheduled ) {
            delete_transient( $kick_key );
        }
    }

    /**
     * AJAX: Run a direct manual export and stream the result.
     */
    public function handle_manual_export_direct(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to export products.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        check_admin_referer( 'prodexfo_manual_export_action', 'nonce' );

        $raw_post = filter_input_array( INPUT_POST, FILTER_UNSAFE_RAW );
        $request  = is_array( $raw_post ) ? wp_unslash( $raw_post ) : [];
        $fields  = $this->exporter->sanitize_fields_from_request( $request );

        if ( empty( $fields ) ) {
            wp_die( esc_html__( 'Select at least one field before exporting.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $this->exporter->remember_selected_fields( $fields );
        $filters  = $this->exporter->sanitize_filters_from_request( $request );
        $format   = $this->exporter->sanitize_format_from_request( $request );
        $settings = $this->exporter->sanitize_settings_from_request( $request );
        $this->exporter->remember_filters( $filters );
        $this->exporter->remember_settings( $settings );

        if ( '' === $format ) {
            $format = $this->exporter->get_default_format_key();
        }

        $upload_dir = wp_upload_dir();

        if ( ! empty( $upload_dir['error'] ) || empty( $upload_dir['basedir'] ) ) {
            wp_die( esc_html__( 'Uploads directory is not writable.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $root_dir = $this->settings_repository->resolve_export_root_directory( (string) ( $upload_dir['basedir'] ?? '' ) );
        $base_dir = trailingslashit( $root_dir . 'manual' );

        if ( ! wp_mkdir_p( $base_dir ) ) {
            wp_die( esc_html__( 'Unable to prepare the export directory.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $resolved_name = $this->exporter->resolve_filename_for_format( $settings, $format );
        $filename      = wp_unique_filename( $base_dir, $resolved_name );
        $file_path     = trailingslashit( $base_dir ) . $filename;

        try {
            $result = $this->exporter->export_to_file( $fields, $filters, $format, $settings, $file_path );
        } catch ( Throwable $exception ) {
            wp_die( esc_html( $exception->getMessage() ) );
        }

        $run_meta = $this->exporter->record_run( $fields, $filters, $format, $result, '', 'manual' );
        do_action( 'prodexfo_export_completed', $fields, $filters, $run_meta );

        if ( ! file_exists( $file_path ) ) {
            wp_die( esc_html__( 'Export file could not be created.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $mtime = filemtime( $file_path );
        DownloadHelper::send_headers( basename( $file_path ), 'application/octet-stream', $mtime ? (int) $mtime : 0 );

        $handle = fopen( $file_path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen

        if ( false === $handle ) {
            wp_die( esc_html__( 'Could not open export file for download.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        while ( ! feof( $handle ) ) {
            echo fread( $handle, 8192 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
        }

        fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        exit;
    }

    /**
     * @param array<string, mixed> $config
     * @return array<string, mixed>
     */
    private function sanitize_action_config( string $type, array $config ): array {
        $config = $this->deep_sanitize_array( $config );

        switch ( $type ) {
            case 'email':
                $recipients = [];

                if ( isset( $config['recipients'] ) ) {
                    $recipients = array_filter( array_map( 'sanitize_email', (array) $config['recipients'] ) );
                }

                if ( ! empty( $recipients ) ) {
                    $config['recipients'] = array_values( array_unique( $recipients ) );
                } else {
                    unset( $config['recipients'] );
                }

                if ( isset( $config['subject'] ) ) {
                    $config['subject'] = sanitize_text_field( (string) $config['subject'] );
                }

                if ( isset( $config['message'] ) ) {
                    $config['message'] = sanitize_textarea_field( (string) $config['message'] );
                }

                if ( isset( $config['headers'] ) && is_array( $config['headers'] ) ) {
                    $config['headers'] = array_filter( array_map( 'sanitize_text_field', $config['headers'] ) );
                }

                break;
            default:
                if ( isset( $config['profile_id'] ) ) {
                    $config['profile_id'] = max( 0, (int) $config['profile_id'] );
                }
                break;
        }

        return $config;
    }

    /**
     * @param array<int|string, mixed> $data
     * @return array<int|string, mixed>
     */
    private function deep_sanitize_array( array $data ): array {
        $sanitized = [];

        foreach ( $data as $key => $value ) {
            $sanitized_key = $key;

            if ( is_string( $key ) ) {
                $sanitized_key = sanitize_key( $key );

                if ( '' === $sanitized_key ) {
                    continue;
                }
            }

            if ( is_array( $value ) ) {
                $sanitized[ $sanitized_key ] = $this->deep_sanitize_array( $value );
                continue;
            }

            if ( is_bool( $value ) ) {
                $sanitized[ $sanitized_key ] = $value;
                continue;
            }

            if ( is_int( $value ) ) {
                $sanitized[ $sanitized_key ] = $value;
                continue;
            }

            if ( is_float( $value ) ) {
                $sanitized[ $sanitized_key ] = $value;
                continue;
            }

            if ( is_scalar( $value ) ) {
                $sanitized[ $sanitized_key ] = sanitize_text_field( (string) $value );
            }
        }

        return $sanitized;
    }

    /**
     * @return array<string, mixed>
     */
    private function get_history_filters(): array {
        $has_filters = false;

        $raw_get = filter_input_array( INPUT_GET, FILTER_UNSAFE_RAW );
        $raw_get = is_array( $raw_get ) ? $raw_get : [];

        $filter_candidates = [
            'history_status'    => isset( $raw_get['history_status'] ) ? sanitize_text_field( (string) $raw_get['history_status'] ) : '',
            'history_template'  => isset( $raw_get['history_template'] ) ? sanitize_text_field( (string) $raw_get['history_template'] ) : '',
            'history_run_type'  => isset( $raw_get['history_run_type'] ) ? sanitize_text_field( (string) $raw_get['history_run_type'] ) : '',
            'history_date_from' => isset( $raw_get['history_date_from'] ) ? sanitize_text_field( (string) $raw_get['history_date_from'] ) : '',
            'history_date_to'   => isset( $raw_get['history_date_to'] ) ? sanitize_text_field( (string) $raw_get['history_date_to'] ) : '',
            'history_per_page'  => isset( $raw_get['history_per_page'] ) ? sanitize_text_field( (string) $raw_get['history_per_page'] ) : '',
            'history_page'      => isset( $raw_get['history_page'] ) ? sanitize_text_field( (string) $raw_get['history_page'] ) : '',
        ];

        foreach ( $filter_candidates as $value ) {
            if ( '' !== $value ) {
                $has_filters = true;
                break;
            }
        }

        $nonce_raw = isset( $raw_get['prodexfo_history_filters_nonce'] )
            ? sanitize_text_field( (string) $raw_get['prodexfo_history_filters_nonce'] )
            : '';

        $nonce_valid = ( ! $has_filters ) || ( '' !== $nonce_raw && wp_verify_nonce( $nonce_raw, 'prodexfo_history_filters' ) );

        $status      = $nonce_valid && isset( $raw_get['history_status'] ) ? sanitize_key( (string) $raw_get['history_status'] ) : '';
        $template_id = $nonce_valid && isset( $raw_get['history_template'] ) ? sanitize_text_field( (string) $raw_get['history_template'] ) : '';
        $run_type    = $nonce_valid && isset( $raw_get['history_run_type'] ) ? sanitize_key( (string) $raw_get['history_run_type'] ) : '';

        $date_from = $nonce_valid && isset( $raw_get['history_date_from'] ) ? sanitize_text_field( (string) $raw_get['history_date_from'] ) : '';
        $date_to   = $nonce_valid && isset( $raw_get['history_date_to'] ) ? sanitize_text_field( (string) $raw_get['history_date_to'] ) : '';

        if ( $date_from && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_from ) ) {
            $date_from = '';
        }

        if ( $date_to && ! preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_to ) ) {
            $date_to = '';
        }

        $per_page_raw = $nonce_valid && isset( $raw_get['history_per_page'] ) ? absint( (string) $raw_get['history_per_page'] ) : 20;
        $per_page     = in_array( $per_page_raw, [ 20, 50 ], true ) ? $per_page_raw : 20;

        $page_raw = $nonce_valid && isset( $raw_get['history_page'] ) ? absint( (string) $raw_get['history_page'] ) : 1;
        $page     = max( 1, $page_raw );
        $offset   = ( $page - 1 ) * $per_page;

        return [
            'status'      => $status,
            'template_id' => $template_id,
            'run_type'    => $run_type,
            'date_from'   => $date_from,
            'date_to'     => $date_to,
            'limit'       => $per_page,
            'offset'      => $offset,
            'page'        => $page,
            'per_page'    => $per_page,
        ];
    }

    /**
     * Register the top-level Badamsoft Product Exporter for WooCommerce menu and its subpages.
     */
    public function register_menu_page(): void {
        $menu_title = 'Badamsoft Product Exporter';

        if ( function_exists( 'prodexfo_is_pro' ) && prodexfo_is_pro() ) {
            $menu_title .= ' PRO';
        }

        add_menu_page(
            $menu_title,
            $menu_title,
            'manage_options',
            self::MENU_SLUG,
            [ $this, 'render_admin_page' ],
            $this->get_menu_icon(),
            55
        );

        add_submenu_page(
            self::MENU_SLUG,
            __( 'Export History', 'badamsoft-product-exporter-for-woocommerce' ),
            __( 'Export History', 'badamsoft-product-exporter-for-woocommerce' ),
            'manage_options',
            self::MENU_HISTORY_SLUG,
            [ $this, 'render_history_page' ]
        );

        $this->hide_submenu_entries();
    }

    /**
     * Hide submenu entries for the main menu page.
     */
    private function hide_submenu_entries(): void {
        remove_submenu_page( self::MENU_SLUG, self::MENU_SLUG );
        remove_submenu_page( self::MENU_SLUG, self::MENU_HISTORY_SLUG );
    }

    /**
     * Handle manual export form submission if present.
     *
     * Reads POST data, performs export to file, records run and streams the file.
     */
    public function maybe_handle_export(): void {
        // Do not run full export logic during AJAX requests (e.g. template CRUD on admin-ajax.php).
        if ( function_exists( 'wp_doing_ajax' ) && wp_doing_ajax() ) {
            return;
        }

        if ( null === filter_input( INPUT_POST, 'wc_product_csv_export', FILTER_UNSAFE_RAW ) ) {
            return;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to export products.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        check_admin_referer( 'prodexfo_manual_export_action', 'prodexfo_manual_export_nonce' );

        $raw_post = filter_input_array( INPUT_POST, FILTER_UNSAFE_RAW );
        $request  = is_array( $raw_post ) ? wp_unslash( $raw_post ) : [];
        $fields  = $this->exporter->sanitize_fields_from_request( $request );

        if ( empty( $fields ) ) {
            $error_url = add_query_arg( 'wc_export_error', 'no_fields', $this->get_page_url() );
            $error_url = add_query_arg(
                'prodexfo_export_error_nonce',
                wp_create_nonce( 'prodexfo_export_error' ),
                $error_url
            );
            wp_safe_redirect( $error_url );
            exit;
        }

        $this->exporter->remember_selected_fields( $fields );
        $filters  = $this->exporter->sanitize_filters_from_request( $request );
        $format   = $this->exporter->sanitize_format_from_request( $request );
        $settings = $this->exporter->sanitize_settings_from_request( $request );
        $this->exporter->remember_filters( $filters );

        $this->exporter->remember_settings( $settings );

        $run_id = $this->scheduled_manager->record_run(
            [
                'run_type'      => 'manual',
                'status'        => 'pending',
                'rows_exported' => 0,
                'file_path'     => null,
                'file_size'     => 0,
                'fields_json'   => wp_json_encode( array_values( $fields ) ),
                'filters_json'  => wp_json_encode( $filters ),
                'settings_json' => wp_json_encode( $settings ),
                'file_format'   => strtoupper( sanitize_key( $format ) ),
                'log'           => RunLogFormatter::formatSuccessLog(
                    [
                        'rows'        => 0,
                        'file_format' => strtoupper( sanitize_key( $format ) ),
                    ]
                ),
            ]
        );

        if ( $run_id <= 0 ) {
            wp_die( esc_html__( 'Failed to start export.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $scheduled = false;

        if ( function_exists( 'as_schedule_single_action' ) ) {
            $action_id = as_schedule_single_action( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } elseif ( function_exists( 'as_enqueue_async_action' ) ) {
            $action_id = as_enqueue_async_action( Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ], 'prodexfo' ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
            $scheduled = is_numeric( $action_id ) && (int) $action_id > 0;
        } else {
            $scheduled = wp_schedule_single_event( time() + 1, Plugin::MANUAL_EXPORT_ASYNC_HOOK, [ $run_id ] ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
        }

        if ( ! $scheduled ) {
            $this->scheduled_manager->update_run_file_meta(
                $run_id,
                [
                    'status'      => 'error',
                    'finished_at' => current_time( 'mysql' ),
                    'log'         => RunLogFormatter::formatFailureLog(
                        [
                            'error' => 'Unable to schedule export run.',
                        ]
                    ),
                ]
            );
            wp_die( esc_html__( 'Unable to schedule export run.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        // Try to kick background processing immediately. On some hosts Action Scheduler / WP-Cron may not run
        // promptly without an explicit trigger.
        if ( class_exists( '\\ActionScheduler_QueueRunner' ) ) {
            try {
                \ActionScheduler_QueueRunner::instance()->maybe_dispatch_async_request();
            } catch ( \Throwable $e ) {
                // Intentionally ignored.
            }
        } elseif ( function_exists( 'spawn_cron' ) ) {
            spawn_cron();
        }

        $redirect_url = add_query_arg( 'prodexfo_async_run_id', (string) $run_id, $this->get_page_url() );
        $redirect_url = add_query_arg( 'prodexfo_async_notice', wp_create_nonce( 'prodexfo_async_export_notice' ), $redirect_url );
        wp_safe_redirect( $redirect_url );
        exit;
    }

    /**
     * Stream a completed manual export file to the browser via admin-ajax.
     */
    public function handle_manual_export_download(): void {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to download exports.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        check_admin_referer( 'prodexfo_manual_export_download', 'nonce' );

        $run_id = absint( filter_input( INPUT_GET, 'run_id', FILTER_UNSAFE_RAW ) );

        if ( $run_id <= 0 ) {
            wp_die( esc_html__( 'Invalid run ID.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $run = $this->scheduled_manager->get_run( $run_id );

        if ( ! $run || ! isset( $run['run_type'] ) || 'manual' !== (string) $run['run_type'] ) {
            wp_die( esc_html__( 'Export run not found.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $file_path = isset( $run['file_path_local'] ) ? (string) $run['file_path_local'] : '';

        if ( '' === $file_path && isset( $run['file_path'] ) ) {
            $file_path = (string) $run['file_path'];
        }

        if ( '' === $file_path || ! file_exists( $file_path ) ) {
            wp_die( esc_html__( 'Export file not found.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $mtime = filemtime( $file_path );
        DownloadHelper::send_headers( basename( $file_path ), 'application/octet-stream', $mtime ? (int) $mtime : 0 );

        $handle = fopen( $file_path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen

        if ( false === $handle ) {
            wp_die( esc_html__( 'Could not open export file for download.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        while ( ! feof( $handle ) ) {
            echo fread( $handle, 8192 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread, WordPress.Security.EscapeOutput.OutputNotEscaped
        }

        fclose( $handle ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        exit;
    }

    /**
     * Render the main React-powered admin page for manual exports.
     */
    public function render_admin_page(): void {
        if ( ! class_exists( 'WooCommerce' ) ) {
            $this->render_missing_wc_notice();
            return;
        }

        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'You do not have permission to run manual exports.', 'badamsoft-product-exporter-for-woocommerce' ) );
        }

        $error_code = '';
        $export_error = filter_input( INPUT_GET, 'wc_export_error', FILTER_UNSAFE_RAW );
        if ( null !== $export_error ) {
            $error_nonce = (string) filter_input( INPUT_GET, 'prodexfo_export_error_nonce', FILTER_UNSAFE_RAW );
            $error_nonce = '' !== $error_nonce ? sanitize_text_field( $error_nonce ) : '';

            if ( '' !== $error_nonce && wp_verify_nonce( $error_nonce, 'prodexfo_export_error' ) ) {
                $error_code = sanitize_key( (string) $export_error );
            }
        }

        if ( 'no_fields' === $error_code ) {
            printf(
                '<div class="notice notice-error"><p>%s</p></div>',
                esc_html__( 'Select at least one field before exporting.', 'badamsoft-product-exporter-for-woocommerce' )
            );
        }

        include $this->plugin->get_view_path( 'react-app.php' );
    }

    /**
     * Enqueue admin assets and bootstrap the initial JS application state.
     *
     * @param string $hook Current admin page hook suffix.
     */
    public function enqueue_admin_assets( string $hook ): void {
        $allowed_hooks = [
            'toplevel_page_' . self::MENU_SLUG,
            self::MENU_SLUG . '_page_' . self::MENU_HISTORY_SLUG,
        ];

        if ( ! in_array( $hook, $allowed_hooks, true ) ) {
            return;
        }

        $disable_external_dequeue = (bool) apply_filters( 'prodexfo_admin_csp_disable_external_dequeue', false, $hook );

        if ( ! $disable_external_dequeue ) {
            add_action(
                'admin_print_styles',
                function () use ( $hook, $allowed_hooks ): void {
                    if ( ! in_array( $hook, $allowed_hooks, true ) ) {
                        return;
                    }

                    $this->dequeue_external_assets_for_csp();
                },
                PHP_INT_MAX
            );

            add_action(
                'admin_print_scripts',
                function () use ( $hook, $allowed_hooks ): void {
                    if ( ! in_array( $hook, $allowed_hooks, true ) ) {
                        return;
                    }

                    $this->dequeue_external_assets_for_csp();
                },
                PHP_INT_MAX
            );

            add_action(
                'admin_enqueue_scripts',
                function ( string $later_hook ) use ( $allowed_hooks ): void {
                    if ( ! in_array( $later_hook, $allowed_hooks, true ) ) {
                        return;
                    }

                    $this->dequeue_external_assets_for_csp();
                },
                PHP_INT_MAX
            );

            if ( ! $this->late_external_dequeue_registered ) {
                $this->late_external_dequeue_registered = true;

                add_action(
                    'admin_print_footer_scripts',
                    function () use ( $allowed_hooks ): void {
                        $screen = get_current_screen();
                        $screen_id = $screen ? (string) $screen->id : '';

                        $is_allowed = false;
                        foreach ( $allowed_hooks as $allowed_hook ) {
                            if ( '' !== $allowed_hook && false !== stripos( $screen_id, $allowed_hook ) ) {
                                $is_allowed = true;
                                break;
                            }
                        }

                        if ( ! $is_allowed ) {
                            return;
                        }

                        $this->dequeue_external_assets_for_csp();
                    },
                    PHP_INT_MAX
                );

                add_action(
                    'admin_footer-' . $hook,
                    function () use ( $allowed_hooks ): void {
                        $screen = get_current_screen();
                        $screen_id = $screen ? (string) $screen->id : '';

                        $is_allowed = false;
                        foreach ( $allowed_hooks as $allowed_hook ) {
                            if ( '' !== $allowed_hook && false !== stripos( $screen_id, $allowed_hook ) ) {
                                $is_allowed = true;
                                break;
                            }
                        }

                        if ( ! $is_allowed ) {
                            return;
                        }

                        $this->dequeue_external_assets_for_csp();
                    },
                    PHP_INT_MAX
                );
            }
        }

        $capabilities          = $this->get_capability_flags();
        $can_manage_templates  = current_user_can( 'manage_options' );
        $can_preview           = current_user_can( 'manage_options' );

        $entry = $this->get_manifest_entry( 'index.html' );

        if ( empty( $entry ) || empty( $entry['file'] ) ) {
            return;
        }

        if ( ! empty( $entry['css'] ) && is_array( $entry['css'] ) ) {
            $inline_fix_added = false;
            foreach ( $entry['css'] as $css_asset ) {
                $css_handle = 'prodexfo-admin-app-' . md5( $css_asset );
                wp_enqueue_style(
                    $css_handle,
                    $this->resolve_asset_url( $css_asset ),
                    [],
                    $this->resolve_asset_version( $css_asset )
                );

                if ( ! $inline_fix_added ) {
                    $inline_fix_added = true;
                    wp_add_inline_style(
                        $css_handle,
                        '#prodexfo-admin-app .grid{display:grid!important;}'
                        . '#prodexfo-admin-app .inline-flex{display:inline-flex!important;}'
                        . '#prodexfo-admin-app .flex{display:flex!important;}'
                        . '@media (min-width:768px){#prodexfo-admin-app .md\\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))!important;}}'
                        . '@media (min-width:1024px){#prodexfo-admin-app .lg\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))!important;}}'
                    );
                }
            }
        }

        wp_register_script(
            'prodexfo-admin-app',
            $this->resolve_asset_url( $entry['file'] ),
            [],
            $this->resolve_asset_version( $entry['file'] ),
            true
        );

        wp_script_add_data( 'prodexfo-admin-app', 'type', 'module' );

        wp_enqueue_script( 'prodexfo-admin-app' );

        $initial_screen    = $this->resolve_initial_screen( $hook );
        $is_pro_build      = (bool) apply_filters( 'prodexfo_is_pro_installed', false );

        $wp_timezone = Utils::get_timezone_for_dropdown();

        $rest_base_url = trailingslashit( rest_url( 'wc-pce/v1' ) );
        $rest_base_url = function_exists( 'wp_make_link_relative' ) ? wp_make_link_relative( $rest_base_url ) : $rest_base_url;

        $state = [
            'initialScreen' => $initial_screen,
            'isProBuild'    => $is_pro_build,
            'timezone'      => $wp_timezone,
            'capabilities'  => $capabilities,
            'urls'          => [
                'ajax' => admin_url( 'admin-ajax.php' ),
                'rest' => $rest_base_url,
            ],
            'nonces'        => [
                'templates'    => $can_manage_templates ? wp_create_nonce( 'prodexfo_templates_nonce' ) : '',
                'preview'      => $can_preview ? wp_create_nonce( 'prodexfo_preview_nonce' ) : '',
                'manualExport' => current_user_can( 'manage_options' ) ? wp_create_nonce( 'prodexfo_manual_export_action' ) : '',
                'download'     => current_user_can( 'manage_options' ) ? wp_create_nonce( 'prodexfo_manual_export_download' ) : '',
                'status'       => current_user_can( 'manage_options' ) ? wp_create_nonce( 'prodexfo_manual_export_status' ) : '',
                'rest'         => wp_create_nonce( 'wp_rest' ),
            ],
            'export'        => $this->build_export_state( $can_manage_templates, $can_preview ),
            'history'       => $this->build_history_state(),
            'settings'      => $this->build_settings_state(),
            'notices'       => $this->collect_settings_notices(),
            'strings'       => $this->get_ui_strings(),
        ];

        $state = apply_filters( 'prodexfo_admin_app_state', $state, $hook, $capabilities );

        $state_json = wp_json_encode( $state );

        if ( false === $state_json ) {
            $state_json = '{}';
        }

        if ( false !== $state_json ) {
            wp_add_inline_script( 'prodexfo-admin-app', 'window.PRODEXFO_APP_STATE = ' . $state_json . ';', 'before' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped

            add_action(
                'admin_print_footer_scripts',
                static function () use ( $state_json ): void {
                    printf(
                        '<script id="prodexfo-admin-app-state">window.PRODEXFO_APP_STATE = %s;</script>' . PHP_EOL,
                        $state_json // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                    );
                },
                PHP_INT_MAX
            );
        }

        wp_add_inline_script(
            'prodexfo-admin-app',
            "(function(){\n" .
            "try {\n" .
            "  var params = new URLSearchParams(window.location.search);\n" .
            "  var runId = params.get('prodexfo_async_run_id');\n" .
            "  var noticeNonce = params.get('prodexfo_async_notice') || '';\n" .
            "  if (!runId) { return; }\n" .
            "  if (noticeNonce && window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.strings && window.PRODEXFO_APP_STATE.strings.exportStartNotification) {\n" .
            "    var noticeHost = document.getElementById('prodexfo-admin-notices');\n" .
            "    if (noticeHost) {\n" .
            "      var msg = window.PRODEXFO_APP_STATE.strings.exportStartNotification;\n" .
            "      noticeHost.innerHTML = '<div class=\"notice notice-info is-dismissible\"><p>' + String(msg) + '</p></div>';\n" .
            "    }\n" .
            "  }\n" .
            "  var restBase = (window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.urls && window.PRODEXFO_APP_STATE.urls.rest) ? window.PRODEXFO_APP_STATE.urls.rest : '';\n" .
            "  var restNonce = (window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.nonces && window.PRODEXFO_APP_STATE.nonces.rest) ? window.PRODEXFO_APP_STATE.nonces.rest : '';\n" .
            "  var ajaxBase = (window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.urls && window.PRODEXFO_APP_STATE.urls.ajax) ? window.PRODEXFO_APP_STATE.urls.ajax : '';\n" .
            "  var downloadNonce = (window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.nonces && window.PRODEXFO_APP_STATE.nonces.download) ? window.PRODEXFO_APP_STATE.nonces.download : '';\n" .
            "  var statusNonce = (window.PRODEXFO_APP_STATE && window.PRODEXFO_APP_STATE.nonces && window.PRODEXFO_APP_STATE.nonces.status) ? window.PRODEXFO_APP_STATE.nonces.status : '';\n" .
            "  var endpoint = restBase ? (restBase.replace(/\\/+$/, '') + '/history/' + encodeURIComponent(runId)) : '';\n" .
            "  var attempts = 0;\n" .
            "  var maxAttempts = 360;\n" .
            "  var delay = 2000;\n" .
            "  var useAjax = !endpoint;\n" .
            "  function triggerDownload(run){\n" .
            "    if (run && run.file_url) { window.location.href = run.file_url; return; }\n" .
            "    var resolvedId = (run && run.id) ? run.id : runId;\n" .
            "    if (!ajaxBase || !downloadNonce || !resolvedId) { return; }\n" .
            "    var sep = ajaxBase.indexOf('?') === -1 ? '?' : '&';\n" .
            "    var downloadUrl = ajaxBase + sep + 'action=prodexfo_manual_export_download&run_id=' + encodeURIComponent(resolvedId) + '&nonce=' + encodeURIComponent(downloadNonce);\n" .
            "    window.location.href = downloadUrl;\n" .
            "  }\n" .
            "  function handleRun(run){\n" .
            "    if (!run) { throw new Error('No run'); }\n" .
            "    if (run.status === 'success') { triggerDownload(run); return true; }\n" .
            "    if (run.status === 'error') { return true; }\n" .
            "    return false;\n" .
            "  }\n" .
            "  function pollAjax(){\n" .
            "    if (!ajaxBase || !statusNonce) { throw new Error('AJAX unavailable'); }\n" .
            "    var body = 'action=prodexfo_manual_export_status&run_id=' + encodeURIComponent(runId) + '&nonce=' + encodeURIComponent(statusNonce);\n" .
            "    return fetch(ajaxBase, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, body: body })\n" .
            "      .then(function(r){ return r.json(); })\n" .
            "      .then(function(payload){\n" .
            "        var data = payload && payload.data ? payload.data : null;\n" .
            "        if (!payload || payload.success !== true || !data || !data.run) { throw new Error('No run'); }\n" .
            "        return data.run;\n" .
            "      });\n" .
            "  }\n" .
            "  function pollRest(){\n" .
            "    if (!endpoint) { useAjax = true; return pollAjax(); }\n" .
            "    return fetch(endpoint, { credentials: 'same-origin', headers: restNonce ? { 'X-WP-Nonce': restNonce } : {} })\n" .
            "      .then(function(r){\n" .
            "        if (!r.ok) {\n" .
            "          if ((r.status === 401 || r.status === 403) && ajaxBase && statusNonce) { useAjax = true; throw new Error('__switch_to_ajax__'); }\n" .
            "          throw new Error('REST error');\n" .
            "        }\n" .
            "        return r.json();\n" .
            "      })\n" .
            "      .then(function(data){ if (!data || !data.run) { throw new Error('No run'); } return data.run; });\n" .
            "  }\n" .
            "  function poll(){\n" .
            "    attempts++;\n" .
            "    var promise = useAjax ? pollAjax() : pollRest();\n" .
            "    promise\n" .
            "      .then(function(run){\n" .
            "        if (handleRun(run)) { return; }\n" .
            "        if (attempts < maxAttempts) { setTimeout(poll, delay); }\n" .
            "      })\n" .
            "      .catch(function(err){\n" .
            "        if (err && String(err.message || err) === '__switch_to_ajax__') { useAjax = true; }\n" .
            "        if (attempts < maxAttempts) { setTimeout(poll, delay); }\n" .
            "      });\n" .
            "  }\n" .
            "  setTimeout(poll, 1000);\n" .
            "} catch (e) {}\n" .
            "})();",
            'after'
        );
    }

    private function dequeue_external_assets_for_csp(): void {
        $patterns = [
            'fonts.googleapis.com',
            'fonts.gstatic.com',
            'stats.wp.com/w.js',
            'stats.wp.com',
            'pixel.wp.com',
        ];

        $patterns = apply_filters( 'prodexfo_admin_csp_blocklist_patterns', $patterns );

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

        $site_host = (string) wp_parse_url( site_url(), PHP_URL_HOST );
        $styles    = wp_styles();
        $scripts   = wp_scripts();

        if ( $scripts instanceof \WP_Scripts ) {
            if ( isset( $scripts->registered['webfont-loader-js'] ) ) {
                $scripts->registered['webfont-loader-js']->extra['after'] = [];
                $scripts->registered['webfont-loader-js']->extra['before'] = [];
            }

            wp_dequeue_script( 'webfont-loader-js' );
            wp_deregister_script( 'webfont-loader-js' );
        }

        if ( $styles && is_array( $styles->registered ) && ! empty( $styles->registered ) ) {
            foreach ( array_keys( $styles->registered ) as $handle ) {
                if ( ! isset( $styles->registered[ $handle ] ) ) {
                    continue;
                }

                $src = $styles->registered[ $handle ]->src;
                if ( ! is_string( $src ) || '' === $src ) {
                    continue;
                }

                if ( '//' === substr( $src, 0, 2 ) ) {
                    $src = ( is_ssl() ? 'https:' : 'http:' ) . $src;
                }

                $host = (string) wp_parse_url( $src, PHP_URL_HOST );

                if ( '' === $host || $host === $site_host ) {
                    continue;
                }

                foreach ( $patterns as $pattern ) {
                    if ( is_string( $pattern ) && '' !== $pattern && false !== stripos( $src, $pattern ) ) {
                        wp_dequeue_style( $handle );
                        wp_deregister_style( $handle );
                        break;
                    }
                }
            }
        }

        if ( $scripts && is_array( $scripts->registered ) && ! empty( $scripts->registered ) ) {
            foreach ( array_keys( $scripts->registered ) as $handle ) {
                if ( ! isset( $scripts->registered[ $handle ] ) ) {
                    continue;
                }

                $registered = $scripts->registered[ $handle ];
                if ( isset( $registered->extra['after'] ) && is_array( $registered->extra['after'] ) && ! empty( $registered->extra['after'] ) ) {
                    $after = $registered->extra['after'];
                    $filtered_after = [];
                    foreach ( $after as $snippet ) {
                        if ( ! is_string( $snippet ) || '' === $snippet ) {
                            continue;
                        }

                        if ( false !== stripos( $snippet, 'fonts.googleapis.com' ) || false !== stripos( $snippet, 'fonts.gstatic.com' ) ) {
                            continue;
                        }

                        if ( false !== stripos( $snippet, 'WebFont.load' ) ) {
                            continue;
                        }

                        if ( false !== stripos( $snippet, 'google:' ) && false !== stripos( $snippet, 'families' ) ) {
                            continue;
                        }

                        $filtered_after[] = $snippet;
                    }

                    $scripts->registered[ $handle ]->extra['after'] = $filtered_after;
                }

                $src = $scripts->registered[ $handle ]->src;
                if ( ! is_string( $src ) || '' === $src ) {
                    continue;
                }

                if ( false !== stripos( $src, 'webfontloader' ) ) {
                    wp_dequeue_script( $handle );
                    wp_deregister_script( $handle );
                    continue;
                }

                if ( '//' === substr( $src, 0, 2 ) ) {
                    $src = ( is_ssl() ? 'https:' : 'http:' ) . $src;
                }

                $host = (string) wp_parse_url( $src, PHP_URL_HOST );

                if ( '' === $host || $host === $site_host ) {
                    continue;
                }

                foreach ( $patterns as $pattern ) {
                    if ( is_string( $pattern ) && '' !== $pattern && false !== stripos( $src, $pattern ) ) {
                        wp_dequeue_script( $handle );
                        wp_deregister_script( $handle );
                        break;
                    }
                }
            }
        }
    }

    /**
     * Get all UI strings for the React frontend.
     *
     * @return array<string, mixed>
     */
    private function get_ui_strings(): array {
        $strings = [
            'brandFieldHint'                   => esc_html__( 'Select a brand taxonomy in WooCommerce → Products → Attributes.', 'badamsoft-product-exporter-for-woocommerce' ),
                'selectAll'                        => esc_html__( 'Select all', 'badamsoft-product-exporter-for-woocommerce' ),
                'clearAll'                         => esc_html__( 'Clear all', 'badamsoft-product-exporter-for-woocommerce' ),
                'noWooCommerce'                    => esc_html__( 'WooCommerce must be active to use the Product CSV Exporter.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportQuickTitle'                 => esc_html__( 'Quick Export', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportQuickSubtitle'              => esc_html__( 'Select fields, configure filters, and export your products in seconds', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportResetButton'                => esc_html__( 'Reset', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportReadyLabel'                 => esc_html__( 'Ready to export', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportColumnsSelectedSuffix'      => esc_html__( 'columns selected', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportPreviewButton'              => esc_html__( 'Preview', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportSaveTemplateButton'         => esc_html__( 'Save as Template', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportNowButton'                  => esc_html__( 'Export Now', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportTemplateHintTitle'          => esc_html__( 'Save a template', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportTemplateHintDescription'    => esc_html__( 'You selected many columns—save them as a template to avoid reconfiguration.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportTemplateNotFound'           => esc_html__( 'Template data not found. It may have been removed.', 'badamsoft-product-exporter-for-woocommerce' ),
                /* translators: %s: template name */
                'exportTemplateLoadedWithName'     => esc_html__( 'Template "%s" loaded. Adjust settings before exporting if needed.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportInProgressTitle'            => esc_html__( 'Export in progress', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportInProgressDescription'      => esc_html__( 'We are generating the file. Download will start automatically—please keep the tab open.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportAsyncHistoryNotice'         => esc_html__( 'Экспорт выполняется в фоне. Результат появится в History.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportStartNotification'          => esc_html__( 'Manual export started. The file download will begin shortly.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportStartFailed'                => esc_html__( 'Failed to start export.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportPermissionError'            => esc_html__( 'You do not have permission to run manual exports.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportNonceMissingError'          => esc_html__( 'Export nonce is missing. Please reload the page.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportNoFieldsError'              => esc_html__( 'Select at least one field before exporting.', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportEnvironmentError'           => esc_html__( 'Manual export is not available in this environment.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templatePermissionError'          => esc_html__( 'You do not have permission to manage templates.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateEndpointUnavailable'      => esc_html__( 'Template save endpoint unavailable. Please refresh the page.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateAjaxUnavailable'          => esc_html__( 'Template AJAX endpoint unavailable. Please refresh the page.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateNoColumnsError'           => esc_html__( 'Select at least one column before saving a template.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateSaveFailed'               => esc_html__( 'Failed to save template.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateUpdated'                  => esc_html__( 'Template updated.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateSaved'                    => esc_html__( 'Template saved.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateSavedHintTitle'           => esc_html__( 'Template saved', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateSavedHintDescription'     => esc_html__( 'The template is available on the Templates tab for quick reuse.', 'badamsoft-product-exporter-for-woocommerce' ),
                'templateGuardNoColumns'           => esc_html__( 'Add at least one column before saving a template.', 'badamsoft-product-exporter-for-woocommerce' ),
                // Sidebar menu and promo
                'menuExport'                       => esc_html__( 'Export', 'badamsoft-product-exporter-for-woocommerce' ),
                'menuTemplates'                    => esc_html__( 'Templates', 'badamsoft-product-exporter-for-woocommerce' ),
                'menuHistory'                      => esc_html__( 'History', 'badamsoft-product-exporter-for-woocommerce' ),
                'menuSettings'                     => esc_html__( 'Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'menuHelpDocs'                     => esc_html__( 'Help / Docs', 'badamsoft-product-exporter-for-woocommerce' ),
                'menuUnlockTitle'                  => '',
                'menuUnlockDescription'            => '',
                'menuUnlockCta'                    => '',
                // Quick Export layout blocks
                'exportAvailableFieldsTitle'       => esc_html__( 'Available Fields', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportAvailableFieldsSubtitle'    => esc_html__( 'Select fields to export', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportColumnsTitle'               => esc_html__( 'Export Columns', 'badamsoft-product-exporter-for-woocommerce' ),
                /* translators: %d: number of selected columns */
                'exportColumnsSubtitle'            => esc_html__( 'Drag to reorder · %d columns selected', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportNoFieldsSelectedTitle'      => esc_html__( 'No fields selected', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportNoFieldsSelectedDescription'=> esc_html__( 'Select fields from the left panel to start building your export', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFileFormatTitle'            => esc_html__( 'File Format', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportCsvSettingsTitle'           => esc_html__( 'CSV Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportCsvDelimiterLabel'          => esc_html__( 'Delimiter', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportCsvEncodingLabel'           => esc_html__( 'Encoding', 'badamsoft-product-exporter-for-woocommerce' ),
                // Field group labels in Available Fields sidebar
                'exportFieldGroup_general'         => esc_html__( 'General', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_pricing'         => esc_html__( 'Pricing', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_content'         => esc_html__( 'Content', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_inventory'       => esc_html__( 'Inventory', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_attributes'      => esc_html__( 'Attributes', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_taxonomy'        => esc_html__( 'Taxonomies', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_acf'             => esc_html__( 'Advanced Custom Fields', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_meta'            => esc_html__( 'Meta Fields', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_multilingual'    => esc_html__( 'Multilingual', 'badamsoft-product-exporter-for-woocommerce' ),
                'exportFieldGroup_other'           => esc_html__( 'Other', 'badamsoft-product-exporter-for-woocommerce' ),
                // History screen
                'historyTitle'                     => esc_html__( 'Export History', 'badamsoft-product-exporter-for-woocommerce' ),
                'historySubtitle'                  => esc_html__( 'View and download your previous exports', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyFilterAllExports'          => esc_html__( 'All Exports', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyFilterManual'              => esc_html__( 'Manual', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyDateRangeLabel'            => esc_html__( 'Date range', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyMoreFiltersButton'         => esc_html__( 'More filters', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyTemplateNameLabel'         => esc_html__( 'Template name', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyTemplateAny'               => esc_html__( 'Any template', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyAdvancedFiltersClear'      => esc_html__( 'Clear', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyAdvancedFiltersApply'      => esc_html__( 'Apply filters', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyResetFiltersButton'        => esc_html__( 'Reset filters', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyRefreshingMessage'         => esc_html__( 'Refreshing export history…', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyNoRunsMatch'               => esc_html__( 'No export runs match the selected filters.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnId'                  => esc_html__( 'ID', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnType'                => esc_html__( 'Type', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnTemplate'            => esc_html__( 'Template', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnFormat'              => esc_html__( 'Format', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnProducts'            => esc_html__( 'Products', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnImagesZip'           => esc_html__( 'Images ZIP', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnStatus'              => esc_html__( 'Status', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnDate'                => esc_html__( 'Date', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyColumnActions'             => esc_html__( 'Actions', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkExportSelectedSingular'=> esc_html__( 'export selected', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkExportSelectedPlural'  => esc_html__( 'exports selected', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkSelectPrompt'          => esc_html__( 'Select exports to delete', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkDeletingTitle'         => esc_html__( 'Deleting…', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkDeleteTooltip'         => esc_html__( 'Delete selected exports', 'badamsoft-product-exporter-for-woocommerce' ),
                'historySummaryEmpty'              => esc_html__( 'No exports recorded yet', 'badamsoft-product-exporter-for-woocommerce' ),
                'historySummaryRangePrefix'        => esc_html__( 'Showing', 'badamsoft-product-exporter-for-woocommerce' ),
                'historySummaryRangeOf'            => esc_html__( 'of', 'badamsoft-product-exporter-for-woocommerce' ),
                'historySummaryRangeSuffix'        => esc_html__( 'exports', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPaginationPrevious'        => esc_html__( 'Previous', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPaginationNext'            => esc_html__( 'Next', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPaginationPageLabel'       => esc_html__( 'Page', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyRunTypeManual'             => esc_html__( 'Manual', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyStatus_success'            => esc_html__( 'Success', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyStatus_error'              => esc_html__( 'Error', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyStatus_running'            => esc_html__( 'Running', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyStatus_pending'            => esc_html__( 'Pending', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyStatus_cancelled'          => esc_html__( 'Cancelled', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkSelectAllTooltip'      => esc_html__( 'Select all exports on this page', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyBulkDeselectAllTooltip'    => esc_html__( 'Deselect all exports on this page', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyImagesZipDownload'         => esc_html__( 'Download', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyImagesZipNotAvailable'     => esc_html__( 'Not available', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewHeaderLabel'        => esc_html__( 'Preview', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyLogHeaderLabel'            => esc_html__( 'Log', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyRunLabel'                  => esc_html__( 'Run', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyProductsLabel'             => esc_html__( 'products', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewFileNotAvailable'   => esc_html__( 'Export file is not available for preview.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewLoadFailed'         => esc_html__( 'Unable to load export file for preview.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewUnavailableForArchives' => esc_html__( 'Preview is unavailable for archive files.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewErrorRender'        => esc_html__( 'Failed to render preview.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewLoadingLabel'       => esc_html__( 'Loading preview…', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewNoColumnsLayout'    => esc_html__( 'Unable to determine column layout for this export.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewFooterText'         => esc_html__( 'Previewing data directly from the exported file.', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyPreviewDownloadButton'     => esc_html__( 'Download file', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyCloseButton'               => esc_html__( 'Close', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyClosePreviewAria'          => esc_html__( 'Close preview', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyCloseLogAria'              => esc_html__( 'Close log dialog', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyLogStatusLabel'            => esc_html__( 'Status', 'badamsoft-product-exporter-for-woocommerce' ),
                'historyLogEmptyMessage'           => esc_html__( 'No log entries available for this export.', 'badamsoft-product-exporter-for-woocommerce' ),
                // Settings screen
                'settingsTitle'                     => esc_html__( 'Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsSubtitle'                  => esc_html__( 'Configure export performance and file storage', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsExportFormatLabel'        => esc_html__( 'Default export format', 'badamsoft-product-exporter-for-woocommerce' ),
                // Tabs
                'settingsTabGeneral'                => esc_html__( 'General', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsTabMultilingual'           => esc_html__( 'Multilingual', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsTabDatabase'               => esc_html__( 'Database', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsTabNotifications'          => esc_html__( 'Notifications', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsTabLicense'                => '',
                'settingsTabAdvanced'               => esc_html__( 'Advanced', 'badamsoft-product-exporter-for-woocommerce' ),
                // General settings
                'settingsGeneralTitle'              => esc_html__( 'General Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralDefaultFormatLabel' => esc_html__( 'Default Export Format', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralProductsPerBatchLabel' => esc_html__( 'Products Per Batch', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralProductsPerBatchHelp'  => esc_html__( 'Lower values reduce memory usage but may increase export time', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralExportDirectoryLabel'  => esc_html__( 'Export Directory', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralAutoCleanupLabel'      => esc_html__( 'Auto-delete exports older than 7 days', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsGeneralAutoCleanupHelp'       => esc_html__( 'Only export files and image ZIP archives are deleted. Export history records remain available in the Export History table.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsSaveButtonLabel'          => esc_html__( 'Save Changes', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsSavingLabel'              => esc_html__( 'Saving…', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsSaved'                    => esc_html__( 'Settings saved.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsSaveFailed'               => esc_html__( 'Failed to save settings.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsRestBaseUrlMissing'       => esc_html__( 'REST API base URL is not configured.', 'badamsoft-product-exporter-for-woocommerce' ),
                // Notifications
                'settingsNotificationsTitle'               => esc_html__( 'Notification Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsSaved'               => esc_html__( 'Notification preferences saved.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsExportCompleteLabel' => esc_html__( 'Export completed successfully', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsExportErrorLabel'    => esc_html__( 'Export failed or encountered errors', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsScheduledCompleteLabel' => esc_html__( 'Scheduled export completed', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsScheduledErrorLabel' => esc_html__( 'Scheduled export failed', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsStorageFullLabel'    => esc_html__( 'Storage space running low', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsAdminNoticesTitle'   => esc_html__( 'Admin notices', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsClearNoticesButton'  => esc_html__( 'Clear notices', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsNoNoticesText'       => esc_html__( 'No notices registered for this screen.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsNotificationsNoticeFallback'      => esc_html__( 'Notice', 'badamsoft-product-exporter-for-woocommerce' ),
                // Database
                'settingsDatabaseTitle'                    => esc_html__( 'Database Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseOptimizationTitle'        => esc_html__( 'Database Optimization', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseOptimizationDescription'  => esc_html__( 'Keep your export history clean and your database optimized', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryRetentionLabel'    => esc_html__( 'History Retention', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryOption_all'        => esc_html__( 'Keep all history', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryOption_30'         => esc_html__( 'Last 30 days', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryOption_90'         => esc_html__( 'Last 90 days', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryOption_180'        => esc_html__( 'Last 180 days', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseHistoryOption_365'        => esc_html__( 'Last 365 days', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseAutoOptimizeLabel'        => esc_html__( 'Auto-optimize database tables weekly', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseCleaningLabel'            => esc_html__( 'Cleaning…', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseCleanNowButtonLabel'      => esc_html__( 'Clean Database Now', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseCleanupCompleted'         => esc_html__( 'Database cleanup completed.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsDatabaseCleanupFailed'            => esc_html__( 'Failed to clean database.', 'badamsoft-product-exporter-for-woocommerce' ),
                // Advanced
                'settingsAdvancedTitle'                    => esc_html__( 'Advanced Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedSaved'                    => esc_html__( 'Advanced settings saved.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedSaveFailed'               => esc_html__( 'Failed to save advanced settings.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedReset'                    => esc_html__( 'Advanced settings reset. Save to apply.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedMemoryLimitLabel'         => esc_html__( 'Memory Limit (MB)', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedMemoryLimitHelp'          => esc_html__( 'Used as a soft limit during export batching (64–4096).', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedExecutionTimeoutLabel'    => esc_html__( 'Execution Timeout (seconds)', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedExecutionTimeoutHelp'     => esc_html__( 'Exporter pauses once this soft limit is reached (60–7200).', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedEnableQueryCachingLabel'  => esc_html__( 'Enable query caching', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedDangerZoneTitle'          => esc_html__( 'Danger Zone', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsAdvancedResetAllButton'           => esc_html__( 'Reset All Settings', 'badamsoft-product-exporter-for-woocommerce' ),
                // Multilingual
                'settingsMultilingualTitle'                => esc_html__( 'Multilingual Settings (WPML / Polylang)', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualWpmlNote'             => esc_html__( 'Install and configure WPML or Polylang to include translated product content. Without a multilingual plugin, only column headers will be localized.', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualExportMethodLabel'    => esc_html__( 'Export Method', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualSeparateColumnsTitle'   => esc_html__( 'Separate Columns', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualSeparateColumnsCaption' => esc_html__( 'name_en, name_de, name_fr...', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualSeparateFilesTitle'     => esc_html__( 'Separate Files', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualSeparateFilesCaption'   => esc_html__( 'products-en.csv, products-de.csv...', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsMultilingualActiveLanguagesLabel'   => esc_html__( 'Active Languages', 'badamsoft-product-exporter-for-woocommerce' ),
                'settingsProFeatureMultipleFormatsTitle'       => '',
                'settingsProFeatureMultipleFormatsDescription' => '',
                'settingsProFeatureAllProductTypesTitle'       => '',
                'settingsProFeatureAllProductTypesDescription' => '',
                'settingsProFeatureCompleteDataTitle'          => '',
                'settingsProFeatureCompleteDataDescription'    => '',
                'settingsProFeatureAdvancedFilteringTitle'     => '',
                'settingsProFeatureAdvancedFilteringDescription' => '',
                'settingsProFeatureSmartFieldMappingTitle'     => '',
                'settingsProFeatureSmartFieldMappingDescription' => '',
                'settingsProFeatureBatchProcessingTitle'       => '',
                'settingsProFeatureBatchProcessingDescription' => '',
                'settingsProFeatureDeltaExportTitle'           => '',
                'settingsProFeatureDeltaExportDescription'     => '',
                'settingsProFeatureDetailedLoggingTitle'       => '',
                'settingsProFeatureDetailedLoggingDescription' => '',
                'settingsSubscriptionInfoTitle'                => '',
                'settingsSubscriptionLicenseTypeLabel'         => '',
                'settingsSubscriptionLicenseTypeValue'         => '',
                'settingsSubscriptionActivationDateLabel'      => '',
                'settingsSubscriptionEmailLabel'               => '',
                'settingsSubscriptionAutoRenewalLabel'         => '',
                'settingsSubscriptionAutoRenewalEnabledValue'  => '',
                'settingsSubscriptionManageButtonLabel'        => '',
                'plugin'                              => $this->build_plugin_info_strings(),
        ];

        return apply_filters( 'prodexfo_admin_ui_strings', $strings );
    }

    /**
     * @return array<string, string>
     */
    private function get_template_strings(): array {
        return [
            'noTemplateOption'    => esc_html__( '— No template —', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalCreateTitle'    => esc_html__( 'Save new template', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalUpdateTitle'    => esc_html__( 'Update template', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalNameLabel'      => esc_html__( 'Template name', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalDescriptionLabel' => esc_html__( 'Description (optional)', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalSaveButton'     => esc_html__( 'Save template', 'badamsoft-product-exporter-for-woocommerce' ),
            'modalUpdateButton'   => esc_html__( 'Update template', 'badamsoft-product-exporter-for-woocommerce' ),
            'toastSaved'          => esc_html__( 'Template saved.', 'badamsoft-product-exporter-for-woocommerce' ),
            'toastUpdated'        => esc_html__( 'Template updated.', 'badamsoft-product-exporter-for-woocommerce' ),
            'toastDeleted'        => esc_html__( 'Template deleted.', 'badamsoft-product-exporter-for-woocommerce' ),
            'toastImported'       => esc_html__( 'Templates imported.', 'badamsoft-product-exporter-for-woocommerce' ),
            'errorNoTemplate'     => esc_html__( 'Select a template first.', 'badamsoft-product-exporter-for-woocommerce' ),
            'errorNoFile'         => esc_html__( 'Select a CSV file to import.', 'badamsoft-product-exporter-for-woocommerce' ),
            'errorInvalidFile'    => esc_html__( 'Only CSV templates are supported.', 'badamsoft-product-exporter-for-woocommerce' ),
            'errorNameRequired'   => esc_html__( 'Template name is required.', 'badamsoft-product-exporter-for-woocommerce' ),
            'errorTemplateMissing'=> esc_html__( 'Template data missing.', 'badamsoft-product-exporter-for-woocommerce' ),
            'genericError'        => esc_html__( 'Something went wrong.', 'badamsoft-product-exporter-for-woocommerce' ),
            'templateAjaxUnavailable' => esc_html__( 'Template AJAX endpoint unavailable. Please refresh the page.', 'badamsoft-product-exporter-for-woocommerce' ),
            'templateSaveFailed'  => esc_html__( 'Failed to save template.', 'badamsoft-product-exporter-for-woocommerce' ),
            'confirmDelete'       => esc_html__( 'Delete the selected template?', 'badamsoft-product-exporter-for-woocommerce' ),
            'statusApplied'       => esc_html__( 'Template applied.', 'badamsoft-product-exporter-for-woocommerce' ),
            // Templates screen UI
            'templatesTitle'      => esc_html__( 'Export Templates', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesSubtitle'   => esc_html__( 'Save and reuse your export configurations', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesSearchPlaceholder' => esc_html__( 'Search templates...', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesCreateButton' => esc_html__( 'Create Template', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesImportingLabel' => esc_html__( 'Importing', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesImportButton' => esc_html__( 'Import', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesDropHint'   => esc_html__( 'Drag a CSV export file here or select it manually.', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesChooseFileButton' => esc_html__( 'Choose CSV File', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesReplaceExistingLabel' => esc_html__( 'Replace templates with the same ID', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesEmptyTitle' => esc_html__( 'No templates found', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesEmptySubtitle' => esc_html__( 'Create your first export template to get started', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesFormatLabel'=> esc_html__( 'Format:', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesFieldsLabel'=> esc_html__( 'Fields:', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesColumnsSuffix' => esc_html__( 'columns', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesUpdatedPrefix' => esc_html__( 'Updated', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesExportButton' => esc_html__( 'Export', 'badamsoft-product-exporter-for-woocommerce' ),
            'templatesProTitle'   => '',
            'templatesProDescription' => '',
            'templatesProButton'  => '',
            // Templates actions & errors
            'actionsUnavailable'  => esc_html__( 'Template actions unavailable. Please refresh the page.', 'badamsoft-product-exporter-for-woocommerce' ),
            'actionsUnavailableReload' => esc_html__( 'Template actions are unavailable. Please reload the page.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importInvalidFile'   => esc_html__( 'Only CSV templates are supported.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importFailedInternal'=> esc_html__( 'Failed to import templates.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importFileNoData'    => esc_html__( 'The file does not contain template data.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importParseFailed'   => esc_html__( 'Unable to parse templates from file.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importSuccessWithQuickExport' => esc_html__( 'Templates imported. First template loaded into Quick Export; save it if needed.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importSuccess'       => esc_html__( 'Templates imported.', 'badamsoft-product-exporter-for-woocommerce' ),
            'importFailed'        => esc_html__( 'Template import failed.', 'badamsoft-product-exporter-for-woocommerce' ),
            'exportEndpointUnavailable' => esc_html__( 'Export endpoint is not available. Please reload the page.', 'badamsoft-product-exporter-for-woocommerce' ),
            'duplicateOriginalNotFound' => esc_html__( 'Original template not found.', 'badamsoft-product-exporter-for-woocommerce' ),
            'previewUnavailable'  => esc_html__( 'Preview is unavailable. Please refresh the page.', 'badamsoft-product-exporter-for-woocommerce' ),
            'previewNoColumns'    => esc_html__( 'Template has no columns selected for preview.', 'badamsoft-product-exporter-for-woocommerce' ),
            'previewPrepareFailed'=> esc_html__( 'Unable to prepare fields for preview.', 'badamsoft-product-exporter-for-woocommerce' ),
            'deleteFailed'        => esc_html__( 'Failed to delete template.', 'badamsoft-product-exporter-for-woocommerce' ),
            'applyFailed'         => esc_html__( 'Failed to apply template.', 'badamsoft-product-exporter-for-woocommerce' ),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function get_schedule_config(): array {
        return [
            'strings' => [
                'editAction'        => esc_html__( 'Edit', 'badamsoft-product-exporter-for-woocommerce' ),
                'cancelAction'      => esc_html__( 'Cancel', 'badamsoft-product-exporter-for-woocommerce' ),
                // Schedule modal UI
                'modalTitleEditSchedule'   => esc_html__( 'Edit Schedule', 'badamsoft-product-exporter-for-woocommerce' ),
                'modalTitleCreateSchedule' => esc_html__( 'Create Schedule', 'badamsoft-product-exporter-for-woocommerce' ),
                'fieldScheduleNameLabel'   => esc_html__( 'Schedule name', 'badamsoft-product-exporter-for-woocommerce' ),
                'fieldExportTemplateLabel' => esc_html__( 'Export Template', 'badamsoft-product-exporter-for-woocommerce' ),
                'fieldExportTemplatePlaceholder' => esc_html__( 'Select a template...', 'badamsoft-product-exporter-for-woocommerce' ),
                'fieldFrequencyLabel'      => esc_html__( 'Frequency', 'badamsoft-product-exporter-for-woocommerce' ),
                'frequencyWeeklyLabel'     => esc_html__( 'Weekly', 'badamsoft-product-exporter-for-woocommerce' ),
                'frequencyMonthlyLabel'    => esc_html__( 'Monthly', 'badamsoft-product-exporter-for-woocommerce' ),
                'frequencyManualLabel'     => esc_html__( 'Manual (cron expression)', 'badamsoft-product-exporter-for-woocommerce' ),
                'frequencyNoneLabel'       => esc_html__( 'None (manual only)', 'badamsoft-product-exporter-for-woocommerce' ),
                'runOnDaysLabel'           => esc_html__( 'Run on days', 'badamsoft-product-exporter-for-woocommerce' ),
                'timeZoneLabel'            => esc_html__( 'Time zone (defaults to WordPress setting)', 'badamsoft-product-exporter-for-woocommerce' ),
                'runAtTimesLabel'          => esc_html__( 'Run at times', 'badamsoft-product-exporter-for-woocommerce' ),
                'addTimeButtonLabel'       => esc_html__( 'Add time', 'badamsoft-product-exporter-for-woocommerce' ),
                'dayOfMonthLabel'          => esc_html__( 'Day of month', 'badamsoft-product-exporter-for-woocommerce' ),
                'cronExpressionLabel'      => esc_html__( 'Cron expression', 'badamsoft-product-exporter-for-woocommerce' ),
                'cronExpressionHelp'       => esc_html__( 'Standard cron syntax: minute hour day month weekday', 'badamsoft-product-exporter-for-woocommerce' ),
                'incrementalLabel'         => esc_html__( 'Incremental Export', 'badamsoft-product-exporter-for-woocommerce' ),
                'incrementalDescription'   => esc_html__( 'Only export products modified since the last run', 'badamsoft-product-exporter-for-woocommerce' ),
                'incrementalOptionLastModified' => esc_html__( 'Based on Last Modified Date', 'badamsoft-product-exporter-for-woocommerce' ),
                'incrementalOptionCreated' => esc_html__( 'Based on Creation Date', 'badamsoft-product-exporter-for-woocommerce' ),
                'activeLabel'              => esc_html__( 'Active', 'badamsoft-product-exporter-for-woocommerce' ),
                'savingLabel'              => esc_html__( 'Saving...', 'badamsoft-product-exporter-for-woocommerce' ),
                'updateScheduleButton'     => esc_html__( 'Update Schedule', 'badamsoft-product-exporter-for-woocommerce' ),
                'createScheduleButton'     => esc_html__( 'Create Schedule', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortSun'          => esc_html__( 'Sun', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortMon'          => esc_html__( 'Mon', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortTue'          => esc_html__( 'Tue', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortWed'          => esc_html__( 'Wed', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortThu'          => esc_html__( 'Thu', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortFri'          => esc_html__( 'Fri', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayShortSat'          => esc_html__( 'Sat', 'badamsoft-product-exporter-for-woocommerce' ),
            ],
            'alerts'  => [
                'loadFailed'               => esc_html__( 'Unable to load schedule data.', 'badamsoft-product-exporter-for-woocommerce' ),
                'invalidCron'              => esc_html__( 'Provide a cron expression.', 'badamsoft-product-exporter-for-woocommerce' ),
                'monthlyTimeRequired'      => esc_html__( 'Add at least one time for the monthly schedule.', 'badamsoft-product-exporter-for-woocommerce' ),
                'weekdayRequired'          => esc_html__( 'Select at least one weekday.', 'badamsoft-product-exporter-for-woocommerce' ),
                'weeklyCustomTimeRequired' => esc_html__( 'Add at least one time slot for the selected days.', 'badamsoft-product-exporter-for-woocommerce' ),
                'weeklyTimeRequired'       => esc_html__( 'Add at least one time for the weekly schedule.', 'badamsoft-product-exporter-for-woocommerce' ),
                'recipientRequired'        => esc_html__( 'Please enter at least one recipient email.', 'badamsoft-product-exporter-for-woocommerce' ),
                // Schedule modal alerts
                'nameTemplateRequired'     => esc_html__( 'Name and Template are required.', 'badamsoft-product-exporter-for-woocommerce' ),
                'monthlyDayAndTimeRequired'=> esc_html__( 'Select at least one day and time for the monthly schedule.', 'badamsoft-product-exporter-for-woocommerce' ),
                'saveFailed'               => esc_html__( 'Failed to save schedule.', 'badamsoft-product-exporter-for-woocommerce' ),
                'genericError'             => esc_html__( 'An error occurred.', 'badamsoft-product-exporter-for-woocommerce' ),
            ],
            'actionDefinitions' => [],
            'actionLabels'      => [],
        ];
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function get_schedule_action_definitions(): array {
        return [];
    }

    /**
     * @return array<string, string>
     */
    private function get_schedule_action_labels(): array {
        return [];
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function get_condition_field_options(): array {
        $map    = $this->exporter->get_condition_field_map();
        $labels = [
            'regular_price'  => esc_html__( 'Regular price', 'badamsoft-product-exporter-for-woocommerce' ),
            'sale_price'     => esc_html__( 'Sale price', 'badamsoft-product-exporter-for-woocommerce' ),
            'stock_quantity' => esc_html__( 'Stock quantity', 'badamsoft-product-exporter-for-woocommerce' ),
            'total_sales'    => esc_html__( 'Total sales', 'badamsoft-product-exporter-for-woocommerce' ),
            'review_count'   => esc_html__( 'Review count', 'badamsoft-product-exporter-for-woocommerce' ),
        ];

        $options = [];

        foreach ( $map as $key => $definition ) {
            $options[ $key ] = [
                'label'     => $labels[ $key ] ?? ucwords( str_replace( '_', ' ', $key ) ),
                'valueType' => $definition['value_type'] ?? 'decimal',
                'operators' => $definition['operators'] ?? [],
            ];
        }

        return $options;
    }

    /**
     * Prepare condition builder configuration for JS.
     *
     * @return array<string, array<string, mixed>>
     */
    private function prepare_condition_field_config(): array {
        return $this->get_condition_field_options();
    }

    private function get_manifest_entry( string $entry ): ?array {
        $manifest = $this->get_asset_manifest();

        return isset( $manifest[ $entry ] ) && is_array( $manifest[ $entry ] )
            ? $manifest[ $entry ]
            : null;
    }

    private function get_asset_manifest(): array {
        if ( null !== $this->asset_manifest ) {
            return $this->asset_manifest;
        }

        $manifest_path = trailingslashit( $this->plugin->get_plugin_path() ) . 'assets/build/.vite/manifest.json';

        if ( ! file_exists( $manifest_path ) ) {
            $this->asset_manifest = [];

            return $this->asset_manifest;
        }

        $contents = file_get_contents( $manifest_path );

        if ( false === $contents ) {
            $this->asset_manifest = [];

            return $this->asset_manifest;
        }

        $decoded = json_decode( $contents, true );
        $this->asset_manifest = is_array( $decoded ) ? $decoded : [];

        return $this->asset_manifest;
    }

    private function resolve_asset_url( string $relative ): string {
        return trailingslashit( $this->plugin->get_plugin_url() ) . 'assets/build/' . ltrim( $relative, '/' );
    }

    private function resolve_asset_version( string $relative ): string {
        $path = trailingslashit( $this->plugin->get_plugin_path() ) . 'assets/build/' . ltrim( $relative, '/' );

        if ( file_exists( $path ) ) {
            return (string) filemtime( $path );
        }

        return defined( 'PRODEXFO_VERSION' ) ? PRODEXFO_VERSION : '1.0.0';
    }

    private function resolve_initial_screen( string $hook ): string {
        switch ( $hook ) {
            case 'toplevel_page_' . self::MENU_SLUG:
                return 'export';
            case self::MENU_SLUG . '_page_' . self::MENU_HISTORY_SLUG:
                return 'history';
            default:
                return 'export';
        }
    }

    private function build_export_state( bool $can_manage_templates, bool $can_preview ): array {
        $field_definitions = $this->exporter->get_field_definitions();
        $last_selection    = array_map( [ $this->exporter, 'sanitize_field_key' ], $this->exporter->get_last_selected_fields() );
        $initial_fields    = empty( $last_selection ) ? $this->exporter->get_default_fields() : $last_selection;
        $template_storage  = $can_manage_templates ? $this->template_manager->get_templates() : [];

        return [
            'fieldDefinitions' => $field_definitions,
            'fieldMetadata'    => $this->build_field_metadata( $field_definitions ),
            'initialFields'    => array_values( $initial_fields ),
            'filters'          => $this->exporter->get_saved_filters(),
            'filterOptions'    => [
                'categories'    => $this->filter_manager->get_category_choices(),
                'brands'        => $this->filter_manager->get_brand_choices(),
                'tags'          => $this->filter_manager->get_tag_choices(),
                'discountModes' => [
                    ''              => __( 'Any state', 'badamsoft-product-exporter-for-woocommerce' ),
                    'discounted'    => __( 'Only discounted', 'badamsoft-product-exporter-for-woocommerce' ),
                    'non_discounted' => __( 'Without discount', 'badamsoft-product-exporter-for-woocommerce' ),
                ],
                'imageModes'    => [
                    ''             => __( 'Any state', 'badamsoft-product-exporter-for-woocommerce' ),
                    'with_image'   => __( 'Only with featured image', 'badamsoft-product-exporter-for-woocommerce' ),
                    'without_image' => __( 'Only without featured image', 'badamsoft-product-exporter-for-woocommerce' ),
                ],
                'reviewsModes'  => [
                    ''              => __( 'Any state', 'badamsoft-product-exporter-for-woocommerce' ),
                    'with_reviews'  => __( 'Only products with reviews', 'badamsoft-product-exporter-for-woocommerce' ),
                    'without_reviews' => __( 'Only products without reviews', 'badamsoft-product-exporter-for-woocommerce' ),
                ],
            ],
            'formatOptions'    => $this->exporter->get_format_options(),
            'delimiterOptions' => $this->get_delimiter_options(),
            'encodingOptions'  => $this->get_encoding_options(),
            'fileSettings'     => $this->exporter->get_saved_settings(),
            'conditionBuilder' => [
                'fields'  => $this->prepare_condition_field_config(),
                'initial' => $this->exporter->get_saved_filters()['condition_groups'] ?? [],
            ],
            'templates'        => $can_manage_templates ? [
                'items'    => $this->prepare_template_js_payload( $template_storage ),
                'selected' => $this->template_manager->get_selected_template_for_user( get_current_user_id() ),
                'strings'  => $this->get_template_strings(),
            ] : null,
            'preview'          => $can_preview ? [
                'limit'              => Exporter::PREVIEW_ROW_LIMIT,
                'metaColumnMinWidth' => 240,
                'columnMinWidth'     => 120,
                'action'             => TemplateController::ACTION_PREVIEW,
            ] : null,
            'hasBrandTaxonomy' => (bool) $this->exporter->get_brand_taxonomy(),
        ];
    }

    private function build_field_metadata( array $field_definitions ): array {
        $metadata = [];

        foreach ( $field_definitions as $field_key => $definition ) {
            $metadata[ $field_key ] = [
                'label'      => $definition['label'] ?? $field_key,
                'is_private' => ! empty( $definition['is_private'] ),
            ];
        }

        return $metadata;
    }

    private function build_history_state(): array {
        $filters = $this->get_history_filters();

        $total_runs = $this->scheduled_manager->count_runs(
            [
                'status'      => $filters['status'],
                'template_id' => $filters['template_id'],
                'run_type'    => $filters['run_type'],
                'date_from'   => $filters['date_from'],
                'date_to'     => $filters['date_to'],
            ]
        );

        $per_page = max( 1, (int) $filters['per_page'] );
        $current  = max( 1, (int) $filters['page'] );
        $pages    = max( 1, (int) ceil( $total_runs / $per_page ) );

        return [
            'filters'    => $filters,
            'runs'       => $this->scheduled_manager->get_runs( $filters ),
            'pagination' => [
                'total'    => $total_runs,
                'current'  => $current,
                'per_page' => $per_page,
                'pages'    => $pages,
            ],
        ];
    }

    private function collect_settings_notices(): array {
        $notices = get_settings_errors();

        if ( empty( $notices ) ) {
            return [];
        }

        return array_map(
            static function ( array $notice ): array {
                return [
                    'code'    => $notice['code'] ?? '',
                    'message' => wp_kses_post( $notice['message'] ?? '' ),
                    'type'    => $notice['type'] ?? 'info',
                ];
            },
            $notices
        );
    }

    /**
     * @return array<int, array<string, mixed>>
                'description' => $template['description'] ?? '',
                'updated_at'  => $template['updated_at'] ?? '',
            ];
        }

        usort(
            $items,
            static function ( array $a, array $b ): int {
                $partsA = explode( '-', $a['updated_at'] );
                $partsB = explode( '-', $b['updated_at'] );

                return checkdate( (int) $partsA[1], (int) $partsA[2], (int) $partsA[0] ) - checkdate( (int) $partsB[1], (int) $partsB[2], (int) $partsB[0] );
            }
        );

        return $items;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    private function prepare_template_js_payload( array $templates ): array {
        $items = [];

        foreach ( $templates as $template ) {
            if ( empty( $template['id'] ) ) {
                continue;
            }

            $items[] = [
                'id'          => $template['id'],
                'name'        => $template['name'] ?? __( 'Untitled template', 'badamsoft-product-exporter-for-woocommerce' ),
                'description' => $template['description'] ?? '',
                'updated_at'  => $template['updated_at'] ?? '',
                'fields'      => $template['fields'] ?? [],
                'filters'     => $template['filters'] ?? [],
                'format'      => $template['format'] ?? '',
                'settings'    => $template['settings'] ?? [],
            ];
        }

        return $items;
    }

    /**
     * @return array<string, string>
     */
    private function get_delimiter_options(): array {
        return [
            ','  => __( 'Comma (,)', 'badamsoft-product-exporter-for-woocommerce' ),
            ';'  => __( 'Semicolon (;)', 'badamsoft-product-exporter-for-woocommerce' ),
            "\t" => __( 'Tab', 'badamsoft-product-exporter-for-woocommerce' ),
            '|'  => __( 'Pipe (|)', 'badamsoft-product-exporter-for-woocommerce' ),
        ];
    }

    /**
     * @return array<string, string>
     */
    private function get_encoding_options(): array {
        return [
            'UTF-8'        => __( 'Unicode (UTF-8)', 'badamsoft-product-exporter-for-woocommerce' ),
            'Windows-1251' => __( 'Cyrillic (Windows-1251)', 'badamsoft-product-exporter-for-woocommerce' ),
            'ISO-8859-1'   => __( 'Latin-1 (ISO-8859-1)', 'badamsoft-product-exporter-for-woocommerce' ),
        ];
    }

    private function get_page_url(): string {
        return admin_url( 'admin.php?page=' . self::MENU_SLUG );
    }

    private function render_missing_wc_notice(): void {
        printf(
            '<div class="notice notice-error"><p>%s</p></div>',
            esc_html__( 'WooCommerce must be active to use the Product CSV Exporter.', 'badamsoft-product-exporter-for-woocommerce' )
        );
    }

    /**
     * @return array<string, bool>
     */
    private function get_capability_flags(): array {
        if ( null !== $this->capability_flags ) {
            return $this->capability_flags;
        }

        $this->capability_flags = $this->access_manager->get_capability_flags_for_user();

        return $this->capability_flags;
    }

    private function build_settings_state(): array {
        return $this->settings_repository->get_state();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    /**
     * @return array<string, scalar|null>
     */
    private function build_plugin_info_strings(): array {
        $version      = defined( 'PRODEXFO_VERSION' ) ? (string) PRODEXFO_VERSION : '';
        $plugin = $this->plugin->get_plugin_info_strings();
        $plugin = is_array( $plugin ) ? $plugin : [];

        return [
            'isPro'   => ! empty( $plugin['isPro'] ),
            'version' => $version,
            'domain'  => isset( $plugin['domain'] ) ? (string) $plugin['domain'] : home_url(),
            'status'  => isset( $plugin['status'] ) ? (string) $plugin['status'] : '',
            'message' => isset( $plugin['message'] ) ? (string) $plugin['message'] : '',
        ];
    }

    /**
     * Get the menu icon for the admin menu.
     *
     * Loads SVG from assets/images/menu-icon.svg and returns as base64 data URI.
     * Falls back to dashicons-database-export if file not found.
     *
     * @return string Menu icon (data URI or dashicon class).
     */
    private function get_menu_icon(): string {
        $icon_path = $this->plugin->get_plugin_path() . 'assets/images/menu-icon.svg';

        if ( file_exists( $icon_path ) ) {
            $svg_content = file_get_contents( $icon_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents

            if ( false !== $svg_content ) {
                return 'data:image/svg+xml;base64,' . base64_encode( $svg_content );
            }
        }

        return 'dashicons-database-export';
    }
}
