<?php

/**
 * Redirects Module
 *
 * Handles URL redirection management with support for simple and regex redirects
 *
 * @package ProRank\SEO\Modules\Tools
 * @since   0.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\TechnicalSEO;

use ProRank\SEO\Modules\BaseModule;
use ProRank\SEO\Plugin;
use ProRank\SEO\Core\Cache\RedirectCache;
use ProRank\SEO\Core\Compatibility\CompetitorDetector;

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


/**
 * Redirects module class
 */


class RedirectsModule extends BaseModule {
    
    /**
     * Module slug
     *
     * @var string
     */
    protected string $slug = 'redirects';
    
    /**
     * Module name
     *
     * @var string
     */
    protected string $name = 'Redirect Manager';
    
    /**
     * Module description
     *
     * @var string
     */
    protected string $description = 'Manage URL redirects with support for 301/302 redirects and advanced regex patterns';
    
    /**
     * Module category
     *
     * @var string
     */
    protected string $category = 'tools';
    
    /**
     * Module tier
     *
     * @var string
     */
    protected string $feature_tier = 'free';
    
    /**
     * Parent module
     *
     * @var string|null
     */
    protected ?string $parent_slug = 'technical-seo';
    
    /**
     * Table name cache
     *
     * @var string|null
     */
    private ?string $table_name = null;
    
    /**
     * Initialize module hooks
     *
     * @return void
     */
    public function init_hooks(): void {
        // In SEO conflict safe mode, skip ProRank redirect handling to avoid
        // conflicts with competing SEO plugins (e.g. Rank Math).
        $detector = new CompetitorDetector();
        if ($detector->should_enable_seo_safe_mode()) {
            return;
        }

        // Hook into template_redirect with early priority
        add_action('template_redirect', [$this, 'handle_redirect'], 5);
        
        // Register REST routes
        add_action('rest_api_init', [$this, 'register_rest_routes']);
        
        // Auto-redirect on URL changes (if enabled)
        if (get_option('prorank_auto_redirects_enabled', true)) {
            add_action('post_updated', [$this, 'handle_post_url_change'], 10, 3);
            add_action('before_delete_post', [$this, 'handle_post_deletion']);
        }
        
        // Add redirect chain detection
        add_action('prorank_redirect_created', [$this, 'detect_redirect_chains']);
        add_action('prorank_redirect_updated', [$this, 'detect_redirect_chains']);
        
        // Warm cache on init
        add_action('init', [RedirectCache::class, 'warm_cache'], 20);
        
        // Clear cache on redirect changes
        add_action('prorank_redirect_created', [$this, 'clear_redirect_cache']);
        add_action('prorank_redirect_updated', [$this, 'clear_redirect_cache']);
        add_action('prorank_redirect_deleted', [$this, 'clear_redirect_cache']);
    }
    
    /**
     * Handle redirect logic
     *
     * @return void
     */
    public function handle_redirect(): void {
        // Skip if in admin or doing AJAX
        if (is_admin() || wp_doing_ajax()) {
            return;
        }
        
        // Get current request path
        $request_path = $this->get_request_path();
        if (empty($request_path)) {
            return;
        }

        $exact_match_candidates = $this->build_request_exact_match_candidates($request_path);
        
        // Try to find a matching redirect
        $redirect = $this->find_redirect($request_path, $exact_match_candidates);
        if (!$redirect) {
            return;
        }
        
        // Regex redirects are not available in the free package.
        if ($redirect->is_regex) {
            return;
        }
        
        // Perform the redirect
        $this->perform_redirect($redirect);
    }
    
    /**
     * Get current request path
     *
     * @return string
     */
    private function get_request_path(): string {
        $request_uri = prorank_get_server_var( 'REQUEST_URI' );
        
        // Remove query string
        $path = wp_parse_url($request_uri, PHP_URL_PATH);
        if ($path === false || $path === null) {
            return '';
        }
        
        // Remove home path if in subdirectory
        $home_path = wp_parse_url(home_url(), PHP_URL_PATH);
        if ($home_path && strpos($path, $home_path) === 0) {
            $path = substr($path, strlen($home_path));
        }
        
        // Ensure leading slash
        return '/' . ltrim($path, '/');
    }

    /**
     * Build exact-match lookup candidates for the current request.
     *
     * @param string $request_path Request path without query string.
     * @return array<int, string>
     */
    private function build_request_exact_match_candidates(string $request_path): array {
        $candidates = [];
        $request_uri = prorank_get_server_var('REQUEST_URI');
        $query = wp_parse_url($request_uri, PHP_URL_QUERY);

        if (is_string($query) && $query !== '') {
            $this->add_exact_source_candidates($candidates, $request_path . '?' . $query);
        }

        $this->add_exact_source_candidates($candidates, $request_path);

        return $candidates;
    }

    /**
     * Add normalized exact-match source variants to a lookup list.
     *
     * @param array<int, string> $candidates Candidate list by reference.
     * @param string             $source_url Source URL candidate.
     * @return void
     */
    private function add_exact_source_candidates(array &$candidates, string $source_url): void {
        $source_url = trim($source_url);
        if ($source_url === '') {
            return;
        }

        $variants = [$source_url, $this->normalize_exact_source_url($source_url)];

        $parts = explode('?', $this->normalize_exact_source_url($source_url), 2);
        $path = $parts[0] ?? '';
        $query = $parts[1] ?? '';

        if ($path !== '' && $path !== '/') {
            $trailed = trailingslashit(untrailingslashit($path));
            $variants[] = $query !== '' ? $trailed . '?' . $query : $trailed;
        }

        foreach ($variants as $variant) {
            if ($variant === '' || in_array($variant, $candidates, true)) {
                continue;
            }

            $candidates[] = $variant;
        }
    }

    /**
     * Normalize an exact redirect source into a canonical path form.
     *
     * @param string $source_url Source URL or path.
     * @return string
     */
    private function normalize_exact_source_url(string $source_url): string {
        $source_url = trim(wp_unslash($source_url));
        if ($source_url === '') {
            return '';
        }

        if (preg_match('#^https?://#i', $source_url)) {
            $parts = wp_parse_url($source_url);
            if (is_array($parts)) {
                $path = isset($parts['path']) ? (string) $parts['path'] : '/';
                if (!empty($parts['query'])) {
                    $path .= '?' . $parts['query'];
                }
                $source_url = $path;
            }
        }

        $parts = explode('?', $source_url, 2);
        $path = $parts[0] ?? '';
        $query = $parts[1] ?? '';

        $path = '/' . ltrim($path, '/');
        if ($path !== '/') {
            $path = untrailingslashit($path);
            if ($path === '') {
                $path = '/';
            }
        }

        return $query !== '' ? $path . '?' . $query : $path;
    }

    /**
     * Normalize redirect sources before storage.
     *
     * @param string $source_url Source URL or pattern.
     * @param bool   $is_regex   Whether the source is a regex pattern.
     * @return string
     */
    private function normalize_source_url_for_storage(string $source_url, bool $is_regex): string {
        $source_url = trim(wp_unslash($source_url));
        if ($source_url === '') {
            return '';
        }

        if ($is_regex) {
            return sanitize_text_field($source_url);
        }

        return sanitize_text_field($this->normalize_exact_source_url($source_url));
    }

    /**
     * Find an existing exact redirect ID across canonical slash variants.
     *
     * @param string   $table      Redirect table name.
     * @param string   $source_url Source URL or path.
     * @param int|null $exclude_id Optional redirect ID to exclude.
     * @return int
     */
    private function find_existing_exact_redirect_id(string $table, string $source_url, ?int $exclude_id = null): int {
        global $wpdb;

        $candidates = [];
        $this->add_exact_source_candidates($candidates, $source_url);
        if (empty($candidates)) {
            return 0;
        }

        $placeholders = implode(', ', array_fill(0, count($candidates), '%s'));
        $args = $candidates;

        $sql = "SELECT id FROM {$table} WHERE source_url IN ({$placeholders})";
        if ($exclude_id !== null) {
            $sql .= ' AND id != %d';
            $args[] = $exclude_id;
        }

        $sql .= ' LIMIT 1';

        return (int) $wpdb->get_var($wpdb->prepare($sql, ...$args));
    }

    /**
     * Normalize a redirect target for same-site chain detection.
     *
     * @param string $target_url Target URL or path.
     * @return string
     */
    private function normalize_internal_target_for_lookup(string $target_url): string {
        if ($target_url === '') {
            return '';
        }

        if (strpos($target_url, 'http://') !== 0 && strpos($target_url, 'https://') !== 0) {
            return $this->normalize_exact_source_url($target_url);
        }

        $target_host = wp_parse_url($target_url, PHP_URL_HOST);
        $home_host = wp_parse_url(home_url('/'), PHP_URL_HOST);

        if (!$target_host || !$home_host || strtolower($target_host) !== strtolower($home_host)) {
            return $target_url;
        }

        $path = wp_parse_url($target_url, PHP_URL_PATH) ?: '/';
        $query = wp_parse_url($target_url, PHP_URL_QUERY);
        if (!empty($query)) {
            $path .= '?' . $query;
        }

        return $this->normalize_exact_source_url($path);
    }
    
    /**
     * Find matching redirect for request path
     *
     * @param string $request_path
     * @return object|null
     */
    private function find_redirect(string $request_path, array $exact_match_candidates = []): ?object {
        if (empty($exact_match_candidates)) {
            $exact_match_candidates = [$request_path];
        }

        foreach ($exact_match_candidates as $candidate) {
            $cached = RedirectCache::get($candidate);
            if ($cached !== null) {
                return $cached;
            }
        }
        
        global $wpdb;
        
        $table = $this->get_table_name();
        
        // Check if table exists first
        if (!$this->table_exists()) {
            return null;
        }
        
        // First try exact match
        $placeholders = implode(', ', array_fill(0, count($exact_match_candidates), '%s'));
        $redirect = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table}
                WHERE source_url IN ({$placeholders})
                AND status = 'active'
                AND is_regex = 0
                LIMIT 1",
                ...$exact_match_candidates
            )
        );
        
        if ($redirect) {
            foreach ($exact_match_candidates as $candidate) {
                RedirectCache::set($candidate, $redirect);
            }
            return $redirect;
        }
        
        return null;
    }
    
    /**
     * Perform the redirect
     *
     * @param object $redirect
     * @return void
     */
    private function perform_redirect(object $redirect): void {
        // Perform safe redirect
        wp_safe_redirect($redirect->target_url, (int) $redirect->type);
        exit();
    }
    
    /**
     * Update redirect statistics
     *
     * @param int $redirect_id
     * @return void
     */
    private function update_redirect_stats(int $redirect_id): void {
        global $wpdb;
        
        $table = $this->get_table_name();
        
        $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$table} 
                SET hits = hits + 1, 
                    last_hit_timestamp = %s 
                WHERE id = %d",
                current_time('mysql'),
                $redirect_id
            )
        );
    }
    
    /**
     * Clear redirect cache
     *
     * @param int $redirect_id Redirect ID (optional)
     * @return void
     */
    public function clear_redirect_cache(int $redirect_id = 0): void {
        if ($redirect_id > 0) {
            // Clear specific redirect from cache
            global $wpdb;
            $table = $this->get_table_name();
            $redirect = $wpdb->get_row(
                $wpdb->prepare(
                    "SELECT source_url, is_regex FROM {$table} WHERE id = %d",
                    $redirect_id
                )
            );

            if ($redirect) {
                RedirectCache::delete($redirect->source_url);

                // 2025 Enhancement: Clear regex list cache if this is a regex redirect
                if ($redirect->is_regex == 1) {
                    RedirectCache::clear_regex_redirects();
                }
            }
        } else {
            // Clear all redirect cache
            RedirectCache::flush();

            // 2025 Enhancement: Also clear regex redirects list cache
            RedirectCache::clear_regex_redirects();
        }
    }
    
    /**
     * Get table name
     *
     * @return string
     */
    private function get_table_name(): string {
        if ($this->table_name === null) {
            global $wpdb;
            $this->table_name = $wpdb->prefix . 'prorank_redirects';
        }
        
        return $this->table_name;
    }
    
    /**
     * Check if redirects table exists
     *
     * @return bool
     */
    private function table_exists(): bool {
        global $wpdb;
        $table = $this->get_table_name();

        // Check if table exists
        $result = $wpdb->get_var("SHOW TABLES LIKE '$table'");
        return $result === $table;
    }

    /**
     * Ensure redirects tables exist.
     *
     * @return bool
     */
    private function ensure_tables(): bool {
        if ($this->table_exists()) {
            return true;
        }

        if (class_exists('\\ProRank\\SEO\\Database\\RedirectsTableInstaller')) {
            \ProRank\SEO\Database\RedirectsTableInstaller::install();
        }

        return $this->table_exists();
    }

    /**
     * Validate a regex pattern and return detailed error info
     *
     * @param string $pattern The regex pattern to validate
     * @return array{valid: bool, error: string|null}
     */
    private function validate_regex_pattern(string $pattern): array {
        // Check for empty pattern
        if (empty($pattern)) {
            return [
                'valid' => false,
                'error' => __('Regex pattern cannot be empty', 'prorank-seo'),
            ];
        }

        // Check for valid delimiters
        $first_char = $pattern[0];
        $valid_delimiters = ['/', '#', '~', '!', '@', '%', '`'];
        if (!in_array($first_char, $valid_delimiters, true)) {
            return [
                'valid' => false,
                'error' => sprintf(
                    /* translators: %s: the delimiter character used */
                    __('Regex pattern should start with a delimiter (e.g., /pattern/). Found: %1$s', 'prorank-seo'),
                    $first_char
                ),
            ];
        }

        // Test the pattern with error handling
        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_set_error_handler -- Needed to catch invalid regex warnings.
        set_error_handler(function($severity, $message) {
            // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- PHP error handler parameters passed to ErrorException
            throw new \ErrorException(esc_html($message), 0, absint($severity));
        });

        try {
            $result = preg_match($pattern, '');
            restore_error_handler();

            if ($result === false) {
                $error = preg_last_error_msg();
                return [
                    'valid' => false,
                    'error' => sprintf(
                        /* translators: %s: the preg error message */
                        __('Invalid regex pattern: %1$s', 'prorank-seo'),
                        $error
                    ),
                ];
            }

            return ['valid' => true, 'error' => null];
        } catch (\ErrorException $e) {
            restore_error_handler();
            return [
                'valid' => false,
                'error' => sprintf(
                    /* translators: %s: the error message */
                    __('Regex compilation error: %1$s', 'prorank-seo'),
                    $e->getMessage()
                ),
            ];
        }
    }

    /**
     * Register REST routes
     *
     * @return void
     */
    public function register_rest_routes(): void {
        register_rest_route('prorank-seo/v1', '/redirects', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'get_redirects'],
                'permission_callback' => [$this, 'check_permissions'],
            ],
            [
                'methods' => 'POST',
                'callback' => [$this, 'create_redirect'],
                'permission_callback' => [$this, 'check_permissions'],
                'args' => $this->get_redirect_args(),
            ],
        ]);
        
        register_rest_route('prorank-seo/v1', '/redirects/(?P<id>\d+)', [
            [
                'methods' => 'GET',
                'callback' => [$this, 'get_redirect'],
                'permission_callback' => [$this, 'check_permissions'],
            ],
            [
                'methods' => 'PUT',
                'callback' => [$this, 'update_redirect'],
                'permission_callback' => [$this, 'check_permissions'],
                'args' => $this->get_redirect_args(),
            ],
            [
                'methods' => 'DELETE',
                'callback' => [$this, 'delete_redirect'],
                'permission_callback' => [$this, 'check_permissions'],
            ],
        ]);
        
        register_rest_route('prorank-seo/v1', '/redirects/bulk', [
            'methods' => 'POST',
            'callback' => [$this, 'bulk_action'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'action' => [
                    'required' => true,
                    'type' => 'string',
                    'enum' => ['activate', 'deactivate', 'delete'],
                ],
                'ids' => [
                    'required' => true,
                    'type' => 'array',
                    'items' => [
                        'type' => 'integer',
                    ],
                ],
            ],
        ]);
        
        register_rest_route('prorank-seo/v1', '/redirects/export', [
            'methods' => 'GET',
            'callback' => [$this, 'export_redirects'],
            'permission_callback' => [$this, 'check_permissions'],
        ]);
        
        register_rest_route('prorank-seo/v1', '/redirects/import', [
            'methods' => 'POST',
            'callback' => [$this, 'import_redirects'],
            'permission_callback' => [$this, 'check_permissions'],
        ]);
        
        register_rest_route('prorank-seo/v1', '/redirects/check-chains', [
            'methods' => 'GET',
            'callback' => [$this, 'check_all_chains'],
            'permission_callback' => [$this, 'check_permissions'],
        ]);

        register_rest_route('prorank-seo/v1', '/redirects/flatten-chains', [
            'methods' => 'POST',
            'callback' => [$this, 'flatten_redirect_chains'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'redirect_ids' => [
                    'type' => 'array',
                    'default' => [],
                    'items' => [
                        'type' => 'integer',
                    ],
                ],
                'flatten_all' => [
                    'type' => 'boolean',
                    'default' => false,
                ],
            ],
        ]);

        register_rest_route('prorank-seo/v1', '/redirects/test-regex', [
            'methods' => 'POST',
            'callback' => [$this, 'test_regex_pattern'],
            'permission_callback' => [$this, 'check_permissions'],
            'args' => [
                'pattern' => [
                    'required' => true,
                    'type' => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'test_urls' => [
                    'type' => 'array',
                    'default' => [],
                    'items' => [
                        'type' => 'string',
                    ],
                ],
                'target_url' => [
                    'type' => 'string',
                    'default' => '',
                ],
            ],
        ]);
    }
    
    /**
     * Check REST permissions
     *
     * @return bool
     */
    public function check_permissions(): bool {
        return current_user_can('manage_options');
    }
    
    /**
     * Get redirect validation args
     *
     * @return array
     */
    private function get_redirect_args(): array {
        return [
            'source_url' => [
                'required' => true,
                'type' => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'validate_callback' => function($value) {
                    return !empty($value) && strlen($value) <= 2048;
                },
            ],
            'target_url' => [
                'required' => true,
                'type' => 'string',
                'sanitize_callback' => 'esc_url_raw',
                'validate_callback' => function($value) {
                    return !empty($value) && strlen($value) <= 2048;
                },
            ],
            'type' => [
                'type' => 'integer',
                'default' => 301,
                'enum' => [301, 302, 303, 307, 308],
            ],
            'is_regex' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'status' => [
                'type' => 'string',
                'default' => 'active',
                'enum' => ['active', 'inactive'],
            ],
        ];
    }
    
    /**
     * Get redirects
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function get_redirects(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            $response = new \WP_REST_Response([]);
            $response->header('X-WP-Total', '0');
            $response->header('X-WP-TotalPages', '0');
            return $response;
        }

        $table = $this->get_table_name();
        
        // Get parameters
        $page = max(1, (int) $request->get_param('page'));
        $per_page = max(1, min(100, (int) $request->get_param('per_page') ?: 20));
        $search = $request->get_param('search');
        $status = $request->get_param('status');
        $type = $request->get_param('type');
        $orderby = $request->get_param('orderby') ?: 'created_at';
        $order = strtoupper($request->get_param('order') ?: 'DESC');
        
        // Validate order
        if (!in_array($order, ['ASC', 'DESC'], true)) {
            $order = 'DESC';
        }
        
        // Validate orderby
        $allowed_orderby = ['id', 'source_url', 'target_url', 'type', 'status', 'hits', 'created_at', 'last_hit_timestamp'];
        if (!in_array($orderby, $allowed_orderby, true)) {
            $orderby = 'created_at';
        }
        
        // Build query
        $where = '1=1';
        $params = [];
        
        if ($search) {
            $where .= ' AND (source_url LIKE %s OR target_url LIKE %s)';
            $search_like = '%' . $wpdb->esc_like($search) . '%';
            $params[] = $search_like;
            $params[] = $search_like;
        }
        
        // WP REST schema may provide "all" as a default value. Treat it as no filter.
        if ($status && $status !== 'all') {
            $where .= ' AND status = %s';
            $params[] = $status;
        }

        // Apply redirect type filter when requested.
        if ($type && $type !== 'all') {
            $type_int = (int) $type;
            if (in_array($type_int, [301, 302, 303, 307, 308], true)) {
                $where .= ' AND type = %d';
                $params[] = $type_int;
            }
        }
        
        // Get total count
        $count_query = "SELECT COUNT(*) FROM {$table} WHERE {$where}";
        if (!empty($params)) {
            $count_query = $wpdb->prepare($count_query, ...$params); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        }
        $total = (int) $wpdb->get_var($count_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        
        // Get redirects
        $offset = ($page - 1) * $per_page;
        $query = "SELECT * FROM {$table} WHERE {$where} ORDER BY {$orderby} {$order} LIMIT %d OFFSET %d";
        $query_args = array_merge($params, [$per_page, $offset]);
        $prepared_query = $wpdb->prepare($query, ...$query_args); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
        
        $redirects = $wpdb->get_results($prepared_query); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter
        
        // Format redirects
        $formatted_redirects = array_map([$this, 'format_redirect'], $redirects);
        
        $response = new \WP_REST_Response($formatted_redirects);
        $response->header('X-WP-Total', (string) $total);
        $response->header('X-WP-TotalPages', (string) ceil($total / $per_page));
        
        return $response;
    }
    
    /**
     * Get single redirect
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function get_redirect(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $id = (int) $request->get_param('id');
        $table = $this->get_table_name();
        
        $redirect = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $id
            )
        );
        
        if (!$redirect) {
            return new \WP_Error(
                'redirect_not_found',
                __('Redirect not found', 'prorank-seo'),
                ['status' => 404]
            );
        }
        
        return new \WP_REST_Response($this->format_redirect($redirect));
    }
    
    /**
     * Create redirect
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function create_redirect(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $table = $this->get_table_name();
        
        // Get parameters
        $is_regex = (bool) $request->get_param('is_regex');
        $source_url = $this->normalize_source_url_for_storage((string) $request->get_param('source_url'), $is_regex);
        $target_url = $request->get_param('target_url');
        $type = (int) $request->get_param('type');
        $status = $request->get_param('status');

        if ($source_url === '') {
            return new \WP_Error(
                'invalid_source_url',
                __('Please provide a valid source URL', 'prorank-seo'),
                ['status' => 400]
            );
        }
        
        // Validate regex if enabled
        if ($is_regex) {
            return new \WP_Error(
                'regex_not_available_in_free',
                __('Regex redirects are not available in the free plugin.', 'prorank-seo'),
                ['status' => 403]
            );

            // Validate regex pattern
            $validation = $this->validate_regex_pattern($source_url);
            if (!$validation['valid']) {
                return new \WP_Error(
                    'invalid_regex',
                    $validation['error'],
                    ['status' => 400]
                );
            }
        }
        
        // Check for duplicate
        $existing = $is_regex
            ? (int) $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT id FROM {$table} WHERE source_url = %s",
                    $source_url
                )
            )
            : $this->find_existing_exact_redirect_id($table, $source_url);
        
        if ($existing) {
            return new \WP_Error(
                'duplicate_redirect',
                __('A redirect with this source URL already exists', 'prorank-seo'),
                ['status' => 409]
            );
        }
        
        // Insert redirect
        $result = $wpdb->insert(
            $table,
            [
                'source_url' => $source_url,
                'target_url' => $target_url,
                'type' => $type,
                'is_regex' => $is_regex ? 1 : 0,
                'status' => $status,
            ],
            ['%s', '%s', '%d', '%d', '%s']
        );
        
        if ($result === false) {
            return new \WP_Error(
                'db_error',
                __('Failed to create redirect', 'prorank-seo'),
                ['status' => 500]
            );
        }

        do_action('prorank_redirect_created', (int) $wpdb->insert_id);
        
        $redirect = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $wpdb->insert_id
            )
        );
        
        return new \WP_REST_Response(
            $this->format_redirect($redirect),
            201
        );
    }
    
    /**
     * Update redirect
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function update_redirect(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $id = (int) $request->get_param('id');
        $table = $this->get_table_name();
        
        // Check if exists
        $existing = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $id
            )
        );
        
        if (!$existing) {
            return new \WP_Error(
                'redirect_not_found',
                __('Redirect not found', 'prorank-seo'),
                ['status' => 404]
            );
        }
        
        // Prepare update data
        $update_data = [];
        $update_format = [];
        
        if ($request->has_param('source_url')) {
            $is_regex = isset($update_data['is_regex'])
                ? (bool) $update_data['is_regex']
                : (bool) $existing->is_regex;
            $new_source_url = $this->normalize_source_url_for_storage(
                (string) $request->get_param('source_url'),
                $is_regex
            );

            $duplicate = $is_regex
                ? (int) $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT id FROM {$table} WHERE source_url = %s AND id != %d",
                        $new_source_url,
                        $id
                    )
                )
                : $this->find_existing_exact_redirect_id($table, $new_source_url, $id);

            if ($duplicate) {
                return new \WP_Error(
                    'duplicate_redirect',
                    __('A redirect with this source URL already exists', 'prorank-seo'),
                    ['status' => 409]
                );
            }

            $update_data['source_url'] = $new_source_url;
            $update_format[] = '%s';
        }
        
        if ($request->has_param('target_url')) {
            $update_data['target_url'] = $request->get_param('target_url');
            $update_format[] = '%s';
        }
        
        if ($request->has_param('type')) {
            $update_data['type'] = (int) $request->get_param('type');
            $update_format[] = '%d';
        }
        
        if ($request->has_param('is_regex')) {
            $is_regex = (bool) $request->get_param('is_regex');
            
            if ($is_regex) {
                return new \WP_Error(
                    'regex_not_available_in_free',
                    __('Regex redirects are not available in the free plugin.', 'prorank-seo'),
                    ['status' => 403]
                );
            }
            
            // Validate regex pattern if enabling
            if ($is_regex && isset($update_data['source_url'])) {
                $validation = $this->validate_regex_pattern($update_data['source_url']);
                if (!$validation['valid']) {
                    return new \WP_Error(
                        'invalid_regex',
                        $validation['error'],
                        ['status' => 400]
                    );
                }
            }
            
            $update_data['is_regex'] = $is_regex ? 1 : 0;
            $update_format[] = '%d';
        }
        
        if ($request->has_param('status')) {
            $update_data['status'] = $request->get_param('status');
            $update_format[] = '%s';
        }
        
        // Update redirect
        $result = $wpdb->update(
            $table,
            $update_data,
            ['id' => $id],
            $update_format,
            ['%d']
        );
        
        if ($result === false) {
            return new \WP_Error(
                'db_error',
                __('Failed to update redirect', 'prorank-seo'),
                ['status' => 500]
            );
        }

        do_action('prorank_redirect_updated', $id);
        
        $redirect = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $id
            )
        );
        
        return new \WP_REST_Response($this->format_redirect($redirect));
    }
    
    /**
     * Delete redirect
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function delete_redirect(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $id = (int) $request->get_param('id');
        $table = $this->get_table_name();
        
        $result = $wpdb->delete(
            $table,
            ['id' => $id],
            ['%d']
        );
        
        if ($result === false) {
            return new \WP_Error(
                'db_error',
                __('Failed to delete redirect', 'prorank-seo'),
                ['status' => 500]
            );
        }
        
        if ($result === 0) {
            return new \WP_Error(
                'redirect_not_found',
                __('Redirect not found', 'prorank-seo'),
                ['status' => 404]
            );
        }

        do_action('prorank_redirect_deleted', $id);
        
        return new \WP_REST_Response(null, 204);
    }
    
    /**
     * Bulk action handler
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function bulk_action(\WP_REST_Request $request) {
        global $wpdb;
        
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $action = $request->get_param('action');
        $ids = $request->get_param('ids');
        $table = $this->get_table_name();
        
        if (empty($ids)) {
            return new \WP_Error(
                'no_ids',
                __('No redirect IDs provided', 'prorank-seo'),
                ['status' => 400]
            );
        }
        
        // Sanitize IDs
        $ids = array_map('intval', $ids);
        $placeholders = implode(',', array_fill(0, count($ids), '%d'));
        
        switch ($action) {
            case 'activate':
                $result = $wpdb->query(
                    $wpdb->prepare(
                        "UPDATE {$table} SET status = 'active' WHERE id IN ({$placeholders})",
                        ...$ids
                    )
                );
                break;
                
            case 'deactivate':
                $result = $wpdb->query(
                    $wpdb->prepare(
                        "UPDATE {$table} SET status = 'inactive' WHERE id IN ({$placeholders})",
                        ...$ids
                    )
                );
                break;
                
            case 'delete':
                $result = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$table} WHERE id IN ({$placeholders})",
                        ...$ids
                    )
                );
                break;
                
            default:
                return new \WP_Error(
                    'invalid_action',
                    __('Invalid bulk action', 'prorank-seo'),
                    ['status' => 400]
                );
        }
        
        if ($result === false) {
            return new \WP_Error(
                'db_error',
                __('Failed to perform bulk action', 'prorank-seo'),
                ['status' => 500]
            );
        }

        if ($action === 'activate' || $action === 'deactivate') {
            foreach ($ids as $redirect_id) {
                do_action('prorank_redirect_updated', (int) $redirect_id);
            }
        } elseif ($action === 'delete') {
            foreach ($ids as $redirect_id) {
                do_action('prorank_redirect_deleted', (int) $redirect_id);
            }
        }
        
        return new \WP_REST_Response([
            'affected' => $result,
        ]);
    }

    /**
     * Test a regex pattern against test URLs
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function test_regex_pattern(\WP_REST_Request $request) {
        return new \WP_Error(
            'regex_not_available_in_free',
            __('Regex redirects are not available in the free plugin.', 'prorank-seo'),
            ['status' => 403]
        );
    }

    /**
     * Format redirect for response
     *
     * @param object $redirect
     * @return array
     */
    private function format_redirect(object $redirect): array {
        return [
            'id' => (int) $redirect->id,
            'source_url' => $redirect->source_url,
            'target_url' => $redirect->target_url,
            'type' => (int) $redirect->type,
            'is_regex' => (bool) $redirect->is_regex,
            'status' => $redirect->status,
            'hits' => (int) $redirect->hits,
            'last_hit_timestamp' => $redirect->last_hit_timestamp,
            'created_at' => $redirect->created_at,
            'updated_at' => $redirect->updated_at,
        ];
    }
    
    /**
     * Handle post URL changes
     *
     * @param int $post_id Post ID
     * @param \WP_Post $post_after Post object after update
     * @param \WP_Post $post_before Post object before update
     * @return void
     */
    public function handle_post_url_change(int $post_id, \WP_Post $post_after, \WP_Post $post_before): void {
        // Skip auto-saves, revisions, and drafts
        if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
            return;
        }
        
        if ($post_after->post_status !== 'publish' || $post_before->post_status !== 'publish') {
            return;
        }
        
        // Check if URL has changed
        $old_url = get_permalink($post_before);
        $new_url = get_permalink($post_after);
        
        // Also check for slug changes
        if ($post_before->post_name !== $post_after->post_name) {
            $old_path = wp_parse_url($old_url, PHP_URL_PATH);
            $new_path = wp_parse_url($new_url, PHP_URL_PATH);
            
            if ($old_path && $new_path && $old_path !== $new_path) {
                $this->create_auto_redirect($old_path, $new_path, $post_id);
            }
        }
    }
    
    /**
     * Handle post deletion
     *
     * @param int $post_id Post ID
     * @return void
     */
    public function handle_post_deletion(int $post_id): void {
        $post = get_post($post_id);
        
        if (!$post || $post->post_status !== 'publish') {
            return;
        }
        
        // Skip if redirect to trash is disabled
        if (!get_option('prorank_redirect_deleted_posts', get_option('prorank_redirect_deleted_to_home', false))) {
            return;
        }
        
        $old_url = get_permalink($post);
        $old_path = wp_parse_url($old_url, PHP_URL_PATH);
        
        if ($old_path) {
            // Redirect to home page or a configured page
            $target = get_option('prorank_deleted_redirect_target', home_url('/'));
            $this->create_auto_redirect($old_path, $target, $post_id, 'deleted');
        }
    }
    
    /**
     * Create automatic redirect
     *
     * @param string $source_url Source URL
     * @param string $target_url Target URL
     * @param int $post_id Related post ID
     * @param string $reason Reason for redirect
     * @return void
     */
    private function create_auto_redirect(string $source_url, string $target_url, int $post_id, string $reason = 'url_changed'): void {
        global $wpdb;
        
        $table = $this->get_table_name();
        $source_url = $this->normalize_source_url_for_storage($source_url, false);
        if ($source_url === '') {
            return;
        }
        
        // Check if redirect already exists
        $existing = $this->find_existing_exact_redirect_id($table, $source_url);
        
        if ($existing) {
            // Update existing redirect
            $wpdb->update(
                $table,
                [
                    'target_url' => $target_url,
                    'updated_at' => current_time('mysql'),
/* translators: %1$: value, %2$: value */
                    'notes' => sprintf(__('Auto-updated: %1$s (Post ID: %2$d)', 'prorank-seo'), $reason, $post_id),
                ],
                ['id' => $existing],
                ['%s', '%s', '%s'],
                ['%d']
            );
        } else {
            // Create new redirect
            $redirect_type = (int) get_option('prorank_default_redirect_type', 301);
            if (!in_array($redirect_type, [301, 302, 303, 307, 308], true)) {
                $redirect_type = 301;
            }

            $wpdb->insert(
                $table,
                [
                    'source_url' => $source_url,
                    'target_url' => $target_url,
                    'type' => $redirect_type,
                    'status' => 'active',
/* translators: %1$: value, %2$: value */
                    'notes' => sprintf(__('Auto-created: %1$s (Post ID: %2$d)', 'prorank-seo'), $reason, $post_id),
                    'created_by' => get_current_user_id(),
                ],
                ['%s', '%s', '%d', '%s', '%s', '%d']
            );
            
            do_action('prorank_redirect_created', $wpdb->insert_id);
        }
    }
    
    /**
     * Detect redirect chains
     *
     * @param int $redirect_id Redirect ID
     * @return array|null Chain information if detected
     */
    public function detect_redirect_chains(int $redirect_id): ?array {
        global $wpdb;
        
        $table = $this->get_table_name();
        $max_depth = (int) get_option('prorank_max_redirect_chain_depth', 3);
        
        // Get the redirect
        $redirect = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$table} WHERE id = %d",
                $redirect_id
            )
        );
        
        if (!$redirect) {
            return null;
        }
        
        $chain = [$redirect->source_url];
        $current_target = $redirect->target_url;
        $depth = 1;
        
        // Follow the chain
        while ($depth < 10) { // Safety limit
            // Check if target is another redirect source
            $lookup_target = $this->normalize_internal_target_for_lookup($current_target);
            $next_redirect = $wpdb->get_row(
                $wpdb->prepare(
                    "SELECT * FROM {$table} 
                    WHERE source_url = %s 
                    AND status = 'active'
                    AND id != %d",
                    $lookup_target,
                    $redirect_id
                )
            );
            
            if (!$next_redirect) {
                break;
            }
            
            $chain[] = $next_redirect->source_url;
            $current_target = $next_redirect->target_url;
            $depth++;
            
            // Check for loops
            if (in_array($current_target, $chain)) {
                // Redirect loop detected!
                $this->handle_redirect_loop($redirect_id, $chain);
                return [
                    'type' => 'loop',
                    'chain' => $chain,
                    'depth' => $depth,
                ];
            }
        }
        
        if ($depth > $max_depth) {
            // Chain too long
            $this->handle_long_chain($redirect_id, $chain, $depth);
            return [
                'type' => 'long_chain',
                'chain' => $chain,
                'depth' => $depth,
                'final_target' => $current_target,
            ];
        }
        
        return null;
    }
    
    /**
     * Handle redirect loop detection
     *
     * @param int $redirect_id Redirect ID
     * @param array $chain Redirect chain
     * @return void
     */
    private function handle_redirect_loop(int $redirect_id, array $chain): void {
        global $wpdb;
        
        $table = $this->get_table_name();
        
        // Deactivate the redirect to prevent loops
        $wpdb->update(
            $table,
            [
                'status' => 'inactive',
                'notes' => __('Deactivated: Redirect loop detected', 'prorank-seo'),
            ],
            ['id' => $redirect_id],
            ['%s', '%s'],
            ['%d']
        );
        
        // Log the issue
        prorank_log(sprintf(
            'ProRank SEO: Redirect loop detected for redirect ID %d. Chain: %s',
            $redirect_id,
            implode(' -> ', $chain)
        ));
        
        // Add admin notice
        set_transient(
            'prorank_redirect_loop_notice',
/* translators: %1$: value */
            sprintf(__('Redirect loop detected and deactivated for redirect ID: %1$d', 'prorank-seo'), $redirect_id),
            HOUR_IN_SECONDS
        );
    }
    
    /**
     * Handle long redirect chain
     *
     * @param int $redirect_id Redirect ID
     * @param array $chain Redirect chain
     * @param int $depth Chain depth
     * @return void
     */
    private function handle_long_chain(int $redirect_id, array $chain, int $depth): void {
        // Add admin notice about long chain
        set_transient(
            'prorank_long_chain_notice_' . $redirect_id,
            sprintf(
/* translators: %1$: value, %2$: value */
                __('Long redirect chain detected (depth: %1$d) for redirect ID: %2$d. Consider optimizing.', 'prorank-seo'),
                $depth,
                $redirect_id
            ),
            DAY_IN_SECONDS
        );
    }
    
    /**
     * Export redirects REST endpoint
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response
     */
    public function export_redirects(\WP_REST_Request $request): \WP_REST_Response {
        if (!$this->ensure_tables()) {
            return new \WP_REST_Response([
                'csv' => "Source URL,Target URL,Type,Status,Regex,Hits,Created,Updated\n",
                'filename' => 'redirects-' . wp_date('Y-m-d') . '.csv',
            ]);
        }

        $format = $request->get_param('format') ?: 'csv';
        
        if ($format === 'json') {
            global $wpdb;
            $table = $this->get_table_name();
            $redirects = $wpdb->get_results("SELECT * FROM {$table} ORDER BY id");
            
            return new \WP_REST_Response([
                'redirects' => $redirects,
                'count' => count($redirects),
                'exported_at' => current_time('mysql'),
            ]);
        }
        
        $csv = $this->export_redirects_csv();
        
        return new \WP_REST_Response([
            'csv' => $csv,
            'filename' => 'redirects-' . wp_date('Y-m-d') . '.csv',
        ]);
    }
    
    /**
     * Import redirects REST endpoint
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function import_redirects(\WP_REST_Request $request) {
        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $csv_content = $request->get_param('csv');
        $file = $request->get_file_params();
        
        if (!empty($file['file'])) {
            // Handle file upload
            $csv_content = file_get_contents($file['file']['tmp_name']);
        }
        
        if (empty($csv_content)) {
            return new \WP_Error(
                'no_data',
                __('No CSV data provided', 'prorank-seo'),
                ['status' => 400]
            );
        }
        
        $results = $this->import_redirects_csv($csv_content);
        
        return new \WP_REST_Response($results);
    }
    
    /**
     * Check all redirects for chains
     *
     * @return \WP_REST_Response
     */
    public function check_all_chains(): \WP_REST_Response {
        global $wpdb;

        if (!$this->ensure_tables()) {
            return new \WP_REST_Response([
                'total_redirects' => 0,
                'chains_detected' => 0,
                'loops_detected' => 0,
                'chains' => [],
                'loops' => [],
            ]);
        }
        
        $table = $this->get_table_name();
        $redirects = $wpdb->get_results("SELECT id FROM {$table} WHERE status = 'active'");
        
        $chains = [];
        $loops = [];
        
        foreach ($redirects as $redirect) {
            $chain_info = $this->detect_redirect_chains((int) $redirect->id);
            if ($chain_info) {
                if ($chain_info['type'] === 'loop') {
                    $loops[] = $chain_info;
                } else {
                    $chains[] = $chain_info;
                }
            }
        }
        
        return new \WP_REST_Response([
            'total_redirects' => count($redirects),
            'chains_detected' => count($chains),
            'loops_detected' => count($loops),
            'chains' => $chains,
            'loops' => $loops,
        ]);
    }

    /**
     * Flatten redirect chains by updating intermediate redirects to point directly to final target
     *
     * @param \WP_REST_Request $request
     * @return \WP_REST_Response|\WP_Error
     */
    public function flatten_redirect_chains(\WP_REST_Request $request) {
        global $wpdb;

        if (!$this->ensure_tables()) {
            return new \WP_Error(
                'redirects_table_missing',
                __('Redirects storage is not available', 'prorank-seo'),
                ['status' => 500]
            );
        }

        $redirect_ids = $request->get_param('redirect_ids') ?: [];
        $flatten_all = $request->get_param('flatten_all');
        $table = $this->get_table_name();

        // If flatten_all is true, get all redirects that are part of chains
        if ($flatten_all) {
            $redirects = $wpdb->get_results("SELECT id FROM {$table} WHERE status = 'active'");
            $redirect_ids = [];

            foreach ($redirects as $redirect) {
                $chain_info = $this->detect_redirect_chains((int) $redirect->id);
                if ($chain_info && $chain_info['type'] === 'long_chain') {
                    $redirect_ids[] = (int) $redirect->id;
                }
            }
        }

        if (empty($redirect_ids)) {
            return new \WP_REST_Response([
                'flattened' => 0,
                'message' => __('No redirect chains found to flatten', 'prorank-seo'),
            ]);
        }

        $flattened = 0;
        $errors = [];

        foreach ($redirect_ids as $redirect_id) {
            // Get the redirect
            $redirect = $wpdb->get_row(
                $wpdb->prepare(
                    "SELECT * FROM {$table} WHERE id = %d",
                    $redirect_id
                )
            );

            if (!$redirect) {
/* translators: %1$: value */
                $errors[] = sprintf(__('Redirect ID %1$d not found', 'prorank-seo'), $redirect_id);
                continue;
            }

            // Skip regex redirects
            if ($redirect->is_regex) {
/* translators: %1$: value */
                $errors[] = sprintf(__('Redirect ID %1$d is regex-based and cannot be flattened', 'prorank-seo'), $redirect_id);
                continue;
            }

            // Follow the chain to find the final target
            $current_target = $redirect->target_url;
            $visited = [$redirect->source_url];
            $max_depth = 10; // Safety limit
            $depth = 0;

            while ($depth < $max_depth) {
                // Check if target matches another redirect's source
                $lookup_target = $this->normalize_internal_target_for_lookup($current_target);
                $next_redirect = $wpdb->get_row(
                    $wpdb->prepare(
                        "SELECT * FROM {$table} WHERE source_url = %s AND status = 'active' AND is_regex = 0",
                        $lookup_target
                    )
                );

                if (!$next_redirect) {
                    break; // Reached the end of the chain
                }

                // Check for loops
                if (in_array($next_redirect->source_url, $visited)) {
/* translators: %1$: value */
                    $errors[] = sprintf(__('Redirect ID %1$d has a loop and cannot be flattened', 'prorank-seo'), $redirect_id);
                    break;
                }

                $visited[] = $next_redirect->source_url;
                $current_target = $next_redirect->target_url;
                $depth++;
            }

            // Only update if target changed and we didn't hit a loop
            if ($current_target !== $redirect->target_url && !in_array($current_target, $visited)) {
                $result = $wpdb->update(
                    $table,
                    ['target_url' => $current_target, 'updated_at' => current_time('mysql')],
                    ['id' => $redirect_id],
                    ['%s', '%s'],
                    ['%d']
                );

                if ($result !== false) {
                    $flattened++;
                    // Clear cache for this redirect
                    $this->clear_redirect_cache($redirect_id);
                }
            }
        }

        return new \WP_REST_Response([
            'flattened' => $flattened,
            'errors' => $errors,
            'message' => sprintf(
                /* translators: %d: number of flattened redirects */
                __('%1$d redirect chain(s) flattened successfully', 'prorank-seo'),
                $flattened
            ),
        ]);
    }

    /**
     * Export redirects to CSV
     *
     * @return string CSV content
     */
    private function export_redirects_csv(): string {
        global $wpdb;
        
        $table = $this->get_table_name();
        $redirects = $wpdb->get_results("SELECT * FROM {$table} ORDER BY id");
        
        $csv = "Source URL,Target URL,Type,Status,Regex,Hits,Created,Updated\n";
        
        foreach ($redirects as $redirect) {
            $csv .= sprintf(
                '"%s","%s","%d","%s","%s","%d","%s","%s"' . "\n",
                $redirect->source_url,
                $redirect->target_url,
                $redirect->type,
                $redirect->status,
                $redirect->is_regex ? 'Yes' : 'No',
                $redirect->hits,
                $redirect->created_at,
                $redirect->updated_at
            );
        }
        
        return $csv;
    }
    
    /**
     * Import redirects from CSV
     *
     * @param string $csv_content CSV content
     * @return array Import results
     */
    public function import_redirects_csv(string $csv_content): array {
        global $wpdb;
        
        $table = $this->get_table_name();
        $lines = explode("\n", $csv_content);
        $header = str_getcsv(array_shift($lines));
        
        $imported = 0;
        $skipped = 0;
        $errors = [];
        
        foreach ($lines as $line) {
            if (empty(trim($line))) {
                continue;
            }
            
            $data = str_getcsv($line);
            if (count($data) < 4) {
/* translators: %1$: value */
                $errors[] = sprintf(__('Invalid line: %1$s', 'prorank-seo'), $line);
                continue;
            }
            
            $source_url = $this->normalize_source_url_for_storage((string) $data[0], false);
            $target_url = $data[1];
            $type = (int) ($data[2] ?? 301);
            $status = $data[3] ?? 'active';

            if ($source_url === '') {
                $errors[] = sprintf(__('Invalid line: %1$s', 'prorank-seo'), $line);
                continue;
            }
            
            // Check for existing redirect
            $existing = $this->find_existing_exact_redirect_id($table, $source_url);
            
            if ($existing) {
                $skipped++;
                continue;
            }
            
            // Insert redirect
            $result = $wpdb->insert(
                $table,
                [
                    'source_url' => $source_url,
                    'target_url' => $target_url,
                    'type' => $type,
                    'status' => $status,
                    'notes' => __('Imported from CSV', 'prorank-seo'),
                ],
                ['%s', '%s', '%d', '%s', '%s']
            );
            
            if ($result) {
                $imported++;
            } else {
/* translators: %1$: value */
                $errors[] = sprintf(__('Failed to import: %1$s', 'prorank-seo'), $source_url);
            }
        }
        
        return [
            'imported' => $imported,
            'skipped' => $skipped,
            'errors' => $errors,
        ];
    }
}
