<?php

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

class SEO_Fury_Redirects {

    private $table_name;

    public function __construct($table_name) {
        $this->table_name = $table_name;
        $this->init_hooks();
        $this->maybe_add_regex_column();
    }

    /**
     * Add is_regex column to redirects table if it doesn't exist (upgrade path).
     */
    private function maybe_add_regex_column() {
        if (!is_admin()) {
            return;
        }
        $version = get_option('seo_fury_redirects_db_version', '1.0');
        if (version_compare($version, '1.1', '>=')) {
            return;
        }
        global $wpdb;
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name));
        if (!$table_exists) {
            return;
        }
        $col = $wpdb->get_results("SHOW COLUMNS FROM {$this->table_name} LIKE 'is_regex'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        if (empty($col)) {
            $wpdb->query("ALTER TABLE {$this->table_name} ADD COLUMN is_regex tinyint(1) NOT NULL DEFAULT 0 AFTER redirect_type"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        }
        update_option('seo_fury_redirects_db_version', '1.1');
    }

private function init_hooks() {
    add_action('template_redirect', array($this, 'check_redirects'), 1);

    add_action('wp_ajax_seo_add_redirect', array($this, 'ajax_add_redirect'));
    add_action('wp_ajax_seo_delete_redirect', array($this, 'ajax_delete_redirect'));
    add_action('wp_ajax_seo_toggle_redirect', array($this, 'ajax_toggle_redirect'));
    add_action('wp_ajax_seo_load_redirects', array($this, 'ajax_load_redirects'));
    add_action('wp_ajax_seo_update_redirect', array($this, 'ajax_update_redirect'));
    add_action('wp_ajax_seo_create_table', array($this, 'ajax_create_table'));
    add_action('wp_ajax_seo_export_redirects_csv', array($this, 'ajax_export_redirects_csv'));
    add_action('wp_ajax_seo_import_redirects_csv', array($this, 'ajax_import_redirects_csv'));
    add_action('wp_ajax_seo_test_redirect', array($this, 'ajax_test_redirect'));
}

/**
 * Dry-run the redirect matcher for a given URL/path WITHOUT performing the
 * redirect or counting a hit. Returns which rule (if any) would fire.
 *
 * @param string $url URL or path to test.
 * @return array {matched:bool, type?:'exact'|'regex', source?, target?, status?}
 */
public function find_matching_redirect($url) {
    global $wpdb;

    $current = strtok((string) $url, '?');
    $current = urldecode($current);
    // Accept a full URL too — reduce to the path.
    $path = wp_parse_url($current, PHP_URL_PATH);
    if (!empty($path)) {
        $current = $path;
    }
    if ($current === '') {
        $current = '/';
    }
    if ($current[0] !== '/') {
        $current = '/' . $current;
    }

    if (!$wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name))) {
        return array('matched' => false);
    }

    // Exact match (with and without trailing slash)
    $redirect = $wpdb->get_row($wpdb->prepare(
        "SELECT * FROM {$this->table_name} WHERE (source_url = %s OR source_url = %s) AND active = 1 LIMIT 1",
        trailingslashit($current),
        untrailingslashit($current)
    ));
    if ($redirect) {
        return array(
            'matched' => true,
            'type'    => 'exact',
            'source'  => $redirect->source_url,
            'target'  => esc_url_raw($redirect->target_url),
            'status'  => (int) $redirect->redirect_type,
        );
    }

    // Regex match (only if the column exists)
    $col_exists = $wpdb->get_results("SHOW COLUMNS FROM {$this->table_name} LIKE 'is_regex'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    if (!empty($col_exists)) {
        $rules = $wpdb->get_results("SELECT * FROM {$this->table_name} WHERE is_regex = 1 AND active = 1 ORDER BY id ASC"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        foreach ((array) $rules as $rule) {
            $pattern = $rule->source_url;
            if (substr($pattern, 0, 1) !== '/' && substr($pattern, 0, 1) !== '#' && substr($pattern, 0, 1) !== '~') {
                $pattern = '#' . $pattern . '#';
            }
            $matches = array();
            if (@preg_match($pattern, $current, $matches)) {
                $target = $rule->target_url;
                for ($i = 1; $i <= 9; $i++) {
                    if (isset($matches[$i])) {
                        $target = str_replace('$' . $i, $matches[$i], $target);
                    }
                }
                return array(
                    'matched' => true,
                    'type'    => 'regex',
                    'source'  => $rule->source_url,
                    'target'  => esc_url_raw($target),
                    'status'  => (int) $rule->redirect_type,
                );
            }
        }
    }

    return array('matched' => false);
}

/**
 * AJAX: test which redirect rule would fire for a URL.
 */
public function ajax_test_redirect() {
    check_ajax_referer('seo_redirects_nonce', 'nonce');
    if (!current_user_can('manage_options')) {
        wp_send_json_error(array('message' => __('Insufficient permissions', 'seo-fury')));
    }
    $url = isset($_POST['url']) ? sanitize_text_field(wp_unslash($_POST['url'])) : '';
    if ($url === '') {
        wp_send_json_error(array('message' => __('Enter a URL or path to test.', 'seo-fury')));
    }
    wp_send_json_success($this->find_matching_redirect($url));
}

    public function check_redirects() {
    if (defined('REST_REQUEST') && REST_REQUEST) {
        return;
    }

    if (isset($_SERVER['REQUEST_URI']) && strpos(sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])), '/wp-json/') !== false) {
        return;
    }

    if (is_admin()) {
        return;
    }

    global $wpdb;

    // Check if the redirects table exists. Use a transient to avoid SHOW TABLES on every page load.
    $cache_key = 'seo_fury_redirects_table_ok';
    if ( ! get_transient( $cache_key ) ) {
        $table_exists = $wpdb->get_var( $wpdb->prepare( "SHOW TABLES LIKE %s", $this->table_name ) );
        if ( ! $table_exists ) {
            return;
        }
        set_transient( $cache_key, 1, DAY_IN_SECONDS );
    }

    $request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';

    $current_url = strtok($request_uri, '?');

    $current_url = urldecode($current_url);

    $url_with_slash = trailingslashit($current_url);
    $url_without_slash = untrailingslashit($current_url);

    $redirect = $wpdb->get_row($wpdb->prepare(
        "SELECT * FROM {$this->table_name}
        WHERE (source_url = %s OR source_url = %s)
        AND active = 1
        LIMIT 1",
        $url_with_slash,
        $url_without_slash
    ));

    if ($redirect) {
        $wpdb->update(
            $this->table_name,
            array('hits' => $redirect->hits + 1),
            array('id' => $redirect->id),
            array('%d'),
            array('%d')
        );

        // Sanitize target URL. esc_url_raw strips dangerous schemes
        // (javascript:, data:, vbscript:, etc.) while keeping http, https,
        // and site-relative paths intact — which is what the plugin needs
        // for both internal and external redirects.
        $safe_target = esc_url_raw($redirect->target_url);
        if (!empty($safe_target)) {
            wp_redirect($safe_target, (int) $redirect->redirect_type);
            exit;
        }
    }

    // Check regex redirects if no exact match found
    $this->check_regex_redirects($current_url);
}

    /**
     * Check regex-based redirects.
     *
     * @param string $current_url The current URL path.
     */
    private function check_regex_redirects($current_url) {
        global $wpdb;

        // Get all active regex redirects. Use a short transient cache to avoid repeated queries.
        $cache_key = 'seo_fury_regex_redirects';
        $regex_redirects = wp_cache_get($cache_key);

        if (false === $regex_redirects) {
            $col_exists = $wpdb->get_results("SHOW COLUMNS FROM {$this->table_name} LIKE 'is_regex'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            if (empty($col_exists)) {
                return;
            }
            $regex_redirects = $wpdb->get_results(
                "SELECT * FROM {$this->table_name} WHERE is_regex = 1 AND active = 1 ORDER BY id ASC" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
            );
            if (!$regex_redirects) {
                $regex_redirects = array();
            }
            wp_cache_set($cache_key, $regex_redirects, '', 300);
        }

        if (empty($regex_redirects)) {
            return;
        }

        foreach ($regex_redirects as $rule) {
            $pattern = $rule->source_url;
            // Ensure the pattern is delimited
            if (substr($pattern, 0, 1) !== '/' && substr($pattern, 0, 1) !== '#' && substr($pattern, 0, 1) !== '~') {
                $pattern = '#' . $pattern . '#';
            }
            // Suppress warnings for invalid regex
            $matches = array();
            if (@preg_match($pattern, $current_url, $matches)) {
                // Support backreferences in target URL ($1, $2, etc.)
                $target = $rule->target_url;
                for ($i = 1; $i <= 9; $i++) {
                    if (isset($matches[$i])) {
                        $target = str_replace('$' . $i, $matches[$i], $target);
                    }
                }

                $wpdb->update(
                    $this->table_name,
                    array('hits' => $rule->hits + 1),
                    array('id' => $rule->id),
                    array('%d'),
                    array('%d')
                );

                // Sanitize before redirect — backreferences ($1, $2) came from
                // user-controlled URL segments, so the final target could smuggle
                // dangerous schemes if not escaped.
                $safe_target = esc_url_raw($target);
                if (!empty($safe_target)) {
                    // Open-redirect guard: if the rule's target HOST itself is
                    // templated from a backreference (e.g. target "https://$1"),
                    // the destination host is visitor-controlled — confine it to
                    // this site. A static external host (e.g. "https://x.com/$1")
                    // is a legitimate, intentional cross-domain redirect.
                    $orig_host = (string) wp_parse_url($rule->target_url, PHP_URL_HOST);
                    if (strpos($orig_host, '$') !== false) {
                        $safe_target = wp_validate_redirect($safe_target, home_url('/'));
                    }
                    wp_redirect($safe_target, (int) $rule->redirect_type);
                    exit;
                }
            }
        }
    }

public function ajax_add_redirect() {
    check_ajax_referer('seo_redirects_nonce', 'nonce');

    if (!current_user_can('manage_options')) {
        wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
    }

    global $wpdb;

    $core = SEO_Fury_Core::get_instance();
    $pro = $core->get_pro_features();

    $current_count = $wpdb->get_var("SELECT COUNT(*) FROM {$this->table_name}"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is set in constructor, not user input

    if ($pro->is_limit_reached('max_redirects', $current_count)) {
        $limit = $pro->get_limit('max_redirects');
        if (function_exists('seo_fury_hide_branding') && seo_fury_hide_branding()) {
            wp_send_json_error(
                sprintf(
                    /* translators: %d: maximum number of redirects allowed */
                    __('FREE plan limit reached (%d redirects). Upgrade to PRO for unlimited redirects.', 'seo-fury'),
                    $limit
                )
            );
        }
        wp_send_json_error(
            sprintf(
                /* translators: %1$d: maximum number of redirects allowed, %2$s: opening link tag, %3$s: closing link tag */
                __('FREE plan limit reached (%1$d redirects). %2$sUpgrade to PRO%3$s for unlimited redirects.', 'seo-fury'),
                $limit,
                '<a href="https://seo-fury.com/#pricing" target="_blank">',
                '</a>'
            )
        );
    }

    $source = isset($_POST['source']) ? sanitize_text_field($_POST['source']) : '';
    $target = isset($_POST['target']) ? sanitize_text_field($_POST['target']) : '';
    $allowed_types = array(301, 302, 303, 307, 308, 410);
    $type = isset($_POST['type']) ? intval($_POST['type']) : 301;
    if ( ! in_array( $type, $allowed_types, true ) ) { $type = 301; }
    $is_regex = isset($_POST['is_regex']) ? intval($_POST['is_regex']) : 0;

    if (empty($source) || empty($target)) {
        wp_send_json_error(__('Please fill in all fields.', 'seo-fury'));
    }

    // Validate regex pattern if flagged as regex
    if ($is_regex) {
        $test_pattern = $source;
        if (substr($test_pattern, 0, 1) !== '/' && substr($test_pattern, 0, 1) !== '#' && substr($test_pattern, 0, 1) !== '~') {
            $test_pattern = '#' . $test_pattern . '#';
        }
        if (@preg_match($test_pattern, '') === false) {
            wp_send_json_error(__('Invalid regex pattern. Please check the syntax.', 'seo-fury'));
        }
    }

    if (!$is_regex) {
        if ($source !== '/') {
            $source = '/' . trim($source, '/');
        }
    }

    if ($target !== '/' && !$is_regex) {
        $target = '/' . trim($target, '/');
    }

    $exists = $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM {$this->table_name} WHERE source_url = %s",
        $source
    ));

    if ($exists > 0) {
        wp_send_json_error(__('A redirect with this source already exists.', 'seo-fury'));
    }

    $insert_data = array(
        'source_url' => $source,
        'target_url' => $target,
        'redirect_type' => $type,
        'hits' => 0,
        'active' => 1,
        'created' => current_time('mysql'),
    );
    $insert_format = array('%s', '%s', '%d', '%d', '%d', '%s');

    // Add is_regex if column exists
    $col_exists = $wpdb->get_results("SHOW COLUMNS FROM {$this->table_name} LIKE 'is_regex'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
    if (!empty($col_exists)) {
        $insert_data['is_regex'] = $is_regex ? 1 : 0;
        $insert_format[] = '%d';
    }

    $result = $wpdb->insert(
        $this->table_name,
        $insert_data,
        $insert_format
    );

    if ($result) {
        wp_send_json_success(__('Redirect added successfully.', 'seo-fury'));
    } else {
        wp_send_json_error(__('Failed to add redirect.', 'seo-fury') . ' ' . $wpdb->last_error);
    }
}

    public function ajax_delete_redirect() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

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

        if ($id <= 0) {
            wp_send_json_error(__('Invalid ID', 'seo-fury'));
        }

        $result = $wpdb->delete(
            $this->table_name,
            array('id' => $id),
            array('%d')
        );

        if ($result) {
            wp_send_json_success(__('Redirect deleted.', 'seo-fury'));
        } else {
            wp_send_json_error(__('Failed to delete redirect.', 'seo-fury'));
        }
    }

    public function ajax_toggle_redirect() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

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

        if ($id <= 0) {
            wp_send_json_error(__('Invalid ID', 'seo-fury'));
        }

        $result = $wpdb->update(
            $this->table_name,
            array('active' => $active),
            array('id' => $id),
            array('%d'),
            array('%d')
        );

        if ($result !== false) {
            wp_send_json_success(__('Status updated.', 'seo-fury'));
        } else {
            wp_send_json_error(__('Failed to update status.', 'seo-fury'));
        }
    }

    public function ajax_load_redirects() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $this->table_name));

        if (!$table_exists) {
            wp_send_json_error(array(
                'message' => __('Redirects table is not created.', 'seo-fury'),
                'redirects' => array(),
                'total' => 0,
                'active' => 0
            ));
            return;
        }

        $redirects = $wpdb->get_results("SELECT * FROM {$this->table_name} ORDER BY id DESC"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- table name is set in constructor, not user input

        $active_count = 0;
        if (!empty($redirects)) {
            foreach ($redirects as $redirect) {
                if (isset($redirect->active) && $redirect->active == 1) {
                    $active_count++;
                }
            }
        }

        wp_send_json_success(array(
            'redirects' => $redirects ? $redirects : array(),
            'total' => count($redirects),
            'active' => $active_count
        ));
    }

    public function ajax_update_redirect() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;

        $id = isset($_POST['id']) ? intval($_POST['id']) : 0;
        $source = isset($_POST['source']) ? sanitize_text_field($_POST['source']) : '';
        $target = isset($_POST['target']) ? sanitize_text_field($_POST['target']) : '';
        $allowed_types = array(301, 302, 303, 307, 308, 410);
        $type = isset($_POST['type']) ? intval($_POST['type']) : 301;
        if ( ! in_array( $type, $allowed_types, true ) ) { $type = 301; }
        $is_regex = isset($_POST['is_regex']) ? intval($_POST['is_regex']) : 0;

        if ($id <= 0 || empty($source) || empty($target)) {
            wp_send_json_error(__('Please fill in all fields.', 'seo-fury'));
        }

        // Validate regex pattern
        if ($is_regex) {
            $test_pattern = $source;
            if (substr($test_pattern, 0, 1) !== '/' && substr($test_pattern, 0, 1) !== '#' && substr($test_pattern, 0, 1) !== '~') {
                $test_pattern = '#' . $test_pattern . '#';
            }
            if (@preg_match($test_pattern, '') === false) {
                wp_send_json_error(__('Invalid regex pattern. Please check the syntax.', 'seo-fury'));
            }
        }

        if (!$is_regex && $source !== '/') {
            $source = '/' . trim($source, '/');
        }

        if (!$is_regex && $target !== '/') {
            $target = '/' . trim($target, '/');
        }

        $update_data = array(
            'source_url' => $source,
            'target_url' => $target,
            'redirect_type' => $type,
        );
        $update_format = array('%s', '%s', '%d');

        $col_exists = $wpdb->get_results("SHOW COLUMNS FROM {$this->table_name} LIKE 'is_regex'"); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
        if (!empty($col_exists)) {
            $update_data['is_regex'] = $is_regex ? 1 : 0;
            $update_format[] = '%d';
        }

        $result = $wpdb->update(
            $this->table_name,
            $update_data,
            array('id' => $id),
            $update_format,
            array('%d')
        );

        if ($result !== false) {
            wp_send_json_success(__('Redirect updated.', 'seo-fury'));
        } else {
            wp_send_json_error(__('Failed to update redirect.', 'seo-fury') . ' ' . $wpdb->last_error);
        }
    }

    public function ajax_create_table() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE IF NOT EXISTS {$this->table_name} (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            source_url varchar(500) NOT NULL,
            target_url varchar(500) NOT NULL,
            redirect_type int(3) NOT NULL DEFAULT 301,
            is_regex tinyint(1) NOT NULL DEFAULT 0,
            hits bigint(20) UNSIGNED NOT NULL DEFAULT 0,
            active tinyint(1) NOT NULL DEFAULT 1,
            created datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY source_url (source_url(191)),
            KEY active (active)
        ) $charset_collate;";

        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);

        // Clear the table-exists cache so check_redirects() starts working immediately.
        set_transient( 'seo_fury_redirects_table_ok', 1, DAY_IN_SECONDS );

        wp_send_json_success(__('Table created.', 'seo-fury'));
    }

    public function ajax_export_redirects_csv() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        global $wpdb;
        $rows = $wpdb->get_results("SELECT source_url, target_url, redirect_type, active FROM {$this->table_name} ORDER BY id DESC", ARRAY_A); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared

        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=seo-fury-redirects.csv');
        $output = fopen('php://output', 'w'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
        fputcsv($output, array('source_url', 'target_url', 'redirect_type', 'active'));
        foreach ($rows as $row) {
            fputcsv($output, array_map('seo_fury_csv_safe', $row));
        }
        fclose($output); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
        exit;
    }

    public function ajax_import_redirects_csv() {
        check_ajax_referer('seo_redirects_nonce', 'nonce');

        if (!current_user_can('manage_options')) {
            wp_send_json_error(__('Insufficient permissions', 'seo-fury'));
        }

        if (empty($_FILES['file']['tmp_name'])) {
            wp_send_json_error(__('CSV file is required.', 'seo-fury'));
        }

        $file = $_FILES['file']['tmp_name'];
        $handle = fopen($file, 'r'); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
        if (!$handle) {
            wp_send_json_error(__('Failed to read CSV file.', 'seo-fury'));
        }

        global $wpdb;
        $imported = 0;
        $updated = 0;
        $skipped = 0;

        $header = fgetcsv($handle);
        if (!$header || count($header) < 2) {
            fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
            wp_send_json_error(__('Invalid CSV header.', 'seo-fury'));
        }

        while (($data = fgetcsv($handle)) !== false) {
            $source = isset($data[0]) ? trim($data[0]) : '';
            $target = isset($data[1]) ? trim($data[1]) : '';
            $type = isset($data[2]) ? intval($data[2]) : 301;
            $active = isset($data[3]) ? intval($data[3]) : 1;

            if (empty($source) || empty($target)) {
                $skipped++;
                continue;
            }

            if ($source !== '/') {
                $source = '/' . trim($source, '/');
            }
            if ($target !== '/') {
                $target = '/' . trim($target, '/');
            }

            $exists = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$this->table_name} WHERE source_url = %s",
                $source
            ));

            if ($exists) {
                $wpdb->update(
                    $this->table_name,
                    array(
                        'target_url' => $target,
                        'redirect_type' => $type,
                        'active' => $active,
                    ),
                    array('id' => $exists),
                    array('%s', '%d', '%d'),
                    array('%d')
                );
                $updated++;
            } else {
                $wpdb->insert(
                    $this->table_name,
                    array(
                        'source_url' => $source,
                        'target_url' => $target,
                        'redirect_type' => $type,
                        'active' => $active,
                        'hits' => 0,
                        'created' => current_time('mysql')
                    ),
                    array('%s', '%s', '%d', '%d', '%d', '%s')
                );
                $imported++;
            }
        }
        fclose($handle); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose

        wp_send_json_success(array(
            'imported' => $imported,
            'updated' => $updated,
            'skipped' => $skipped
        ));
    }
}
