<?php

/**
 * URL Cleanup REST API Controller
 *
 * @package ProRank\SEO\Core\RestApi
 * @since   0.1.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\RestApi;

use ProRank\SEO\Plugin;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
use WP_Error;

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


/**
 * URL Cleanup Controller class
 */


class UrlCleanupController extends WP_REST_Controller {
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->namespace = 'prorank-seo/v1';
        $this->rest_base = 'settings/url-cleanup';
    }
    
    /**
     * Register routes
     *
     * @return void
     */
    public function register_routes(): void {
        // Get/Update URL cleanup settings
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base,
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_settings'],
                    'permission_callback' => [$this, 'check_permission'],
                ],
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'update_settings'],
                    'permission_callback' => [$this, 'check_permission'],
                    'args'                => $this->get_settings_args(),
                ],
            ]
        );
        
        // Test URL cleanup
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/test',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'test_cleanup'],
                    'permission_callback' => [$this, 'check_permission'],
                    'args'                => [
                        'url' => [
                            'required' => true,
                            'type' => 'string',
                            'format' => 'uri',
                        ],
                    ],
                ],
            ]
        );
        
        // Bulk URL cleanup
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/bulk-cleanup',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'bulk_cleanup'],
                    'permission_callback' => [$this, 'check_permission'],
                ],
            ]
        );

        // Detect redirect chains (Pro feature)
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/detect-chains',
            [
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'detect_redirect_chains'],
                    'permission_callback' => [$this, 'check_permission'],
                ],
            ]
        );

    }
    
    /**
     * Check permissions
     *
     * @param WP_REST_Request $request Request object
     * @return bool|WP_Error
     */
    public function check_permission(WP_REST_Request $request) {
        if (!current_user_can('manage_options')) {
            return new WP_Error(
                'prorank_seo_rest_forbidden',
                __('You do not have permission to access this resource.', 'prorank-seo'),
                ['status' => 403]
            );
        }
        
        return true;
    }
    
    /**
     * Get URL cleanup settings
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function get_settings(WP_REST_Request $request) {
        try {
            $settings = $this->get_persisted_settings();
            
            return rest_ensure_response([
                'success' => true,
                'settings' => $settings,
            ]);
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_seo_rest_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }
    
    /**
     * Update URL cleanup settings
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function update_settings(WP_REST_Request $request) {
        try {
            $settings = $request->get_params();
            
            // Validate settings
            $validated_settings = $this->validate_settings($settings);
            
            // Save individual settings for the module
            foreach ($validated_settings as $key => $value) {
                update_option('prorank_seo_url_cleanup_' . $key, $value);
            }

            try {
                Plugin::get_instance()->settings()->update_settings_group('url_cleanup', $validated_settings);
            } catch (\Throwable $e) {
                // Keep legacy option writes as the compatibility fallback.
            }
            
            // Flush rewrite rules if permalink structure changes
            if (
                isset($settings['strip_category_base']) || 
                isset($settings['strip_tag_base']) ||
                isset($settings['force_trailing_slash']) || 
                isset($settings['remove_trailing_slash'])
            ) {
                flush_rewrite_rules();
            }
            
            return rest_ensure_response([
                'success' => true,
                'message' => __('URL cleanup settings updated successfully.', 'prorank-seo'),
                'settings' => $validated_settings,
            ]);
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_seo_rest_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }
    
    /**
     * Test URL cleanup on a given URL
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function test_cleanup(WP_REST_Request $request) {
        try {
            $test_url = $request->get_param('url');
            
            // Get persisted settings
            $settings = $this->get_persisted_settings();
            
            $original_url = $test_url;
            $rules_applied = [];
            
            // Parse URL
            $parsed = wp_parse_url($test_url);
            if (!$parsed || !isset($parsed['host'])) {
                return new WP_Error(
                    'prorank_seo_invalid_url',
                    __('Invalid URL provided.', 'prorank-seo'),
                    ['status' => 400]
                );
            }
            
            $rules_applied = array_merge($rules_applied, $this->apply_tracking_parameter_cleanup($parsed, $settings));
            
            // Apply trailing slash rules
            if (isset($parsed['path']) && $parsed['path'] !== '/') {
                $has_trailing_slash = substr($parsed['path'], -1) === '/';
                
                if ($settings['force_trailing_slash'] && !$has_trailing_slash) {
                    $parsed['path'] .= '/';
                    $rules_applied[] = __('Added trailing slash', 'prorank-seo');
                } elseif ($settings['remove_trailing_slash'] && $has_trailing_slash) {
                    $parsed['path'] = rtrim($parsed['path'], '/');
                    $rules_applied[] = __('Removed trailing slash', 'prorank-seo');
                }
            }
            
            // Convert to lowercase
            if ($settings['convert_to_lowercase']) {
                if (isset($parsed['path'])) {
                    $lowercase_path = strtolower($parsed['path']);
                    if ($lowercase_path !== $parsed['path']) {
                        $parsed['path'] = $lowercase_path;
                        $rules_applied[] = __('Converted URL to lowercase', 'prorank-seo');
                    }
                }
            }
            
            // Rebuild URL
            $cleaned_url = $this->build_url($parsed);
            
            return rest_ensure_response([
                'success' => true,
                'original' => $original_url,
                'cleaned' => $cleaned_url,
                'original_url' => $original_url,
                'cleaned_url' => $cleaned_url,
                'changes' => $rules_applied,  // Frontend expects 'changes' property
                'redirect_needed' => $original_url !== $cleaned_url,
            ]);
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_seo_rest_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }
    
    /**
     * Bulk cleanup URLs
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function bulk_cleanup(WP_REST_Request $request) {
        try {
            global $wpdb;
            
            $settings = $this->get_persisted_settings();
            $processed = 0;
            $updated = 0;
            
            // Get all posts and pages
            $posts = $wpdb->get_results(
                "SELECT ID, post_name, post_type FROM {$wpdb->posts} 
                 WHERE post_status = 'publish' 
                 AND post_type IN ('post', 'page')"
            );
            
            foreach ($posts as $post) {
                $processed++;
                $new_slug = $post->post_name;
                $changed = false;
                
                // Remove stop words if enabled
                if (!empty($settings['remove_stop_words'])) {
                    $stop_words = [
                        'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',
                        'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'that', 'the',
                        'to', 'was', 'will', 'with', 'this', 'but', 'they', 'have'
                    ];
                    
                    $words = explode('-', $new_slug);
                    $filtered = array_filter($words, function($word) use ($stop_words) {
                        return !in_array(strtolower($word), $stop_words);
                    });
                    
                    $filtered_slug = implode('-', $filtered);
                    if ($filtered_slug !== $new_slug && !empty($filtered_slug)) {
                        $new_slug = $filtered_slug;
                        $changed = true;
                    }
                }
                
                // Convert to lowercase if enabled
                if (!empty($settings['convert_to_lowercase'])) {
                    $lower_slug = strtolower($new_slug);
                    if ($lower_slug !== $new_slug) {
                        $new_slug = $lower_slug;
                        $changed = true;
                    }
                }
                
                // Update if changed
                if ($changed) {
                    // Make sure slug is unique
                    $new_slug = wp_unique_post_slug($new_slug, $post->ID, 'publish', $post->post_type, 0);
                    
                    $wpdb->update(
                        $wpdb->posts,
                        ['post_name' => $new_slug],
                        ['ID' => $post->ID]
                    );
                    
                    $updated++;
                }
            }
            
            // Flush rewrite rules
            if ($updated > 0) {
                flush_rewrite_rules();
            }
            
            return rest_ensure_response([
                'success' => true,
                'message' => sprintf(
/* translators: %1$: value, %2$: value */
                    __('Bulk cleanup completed. Processed %1$d URLs, updated %2$d.', 'prorank-seo'),
                    $processed,
                    $updated
                ),
                'processed' => $processed,
                'updated' => $updated,
            ]);
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_seo_rest_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }

    /**
     * Detect redirect chains (Pro feature)
     *
     * Scans all redirects and identifies chains that could be consolidated
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response|WP_Error
     */
    public function detect_redirect_chains(WP_REST_Request $request) {
        try {
            // Get all redirects from the Redirect Manager
            $redirects = get_option('prorank_seo_redirects', []);

            if (empty($redirects)) {
                return rest_ensure_response([
                    'success' => true,
                    'chains' => [],
                    'message' => __('No redirects found to analyze.', 'prorank-seo'),
                ]);
            }

            // Build a lookup map for quick source-to-destination resolution
            $redirect_map = [];
            foreach ($redirects as $redirect) {
                $source = rtrim($redirect['source'] ?? '', '/');
                $destination = $redirect['destination'] ?? '';
                if ($source && $destination) {
                    $redirect_map[$source] = $destination;
                }
            }

            $chains = [];
            $visited = [];

            foreach ($redirects as $redirect) {
                $source = rtrim($redirect['source'] ?? '', '/');

                // Skip if already processed
                if (isset($visited[$source])) {
                    continue;
                }

                // Trace the chain
                $chain = [$source];
                $current = $source;
                $max_hops = 10; // Prevent infinite loops
                $hop_count = 0;

                while (isset($redirect_map[$current]) && $hop_count < $max_hops) {
                    $next = $redirect_map[$current];
                    $next_normalized = rtrim(wp_parse_url($next, PHP_URL_PATH) ?? $next, '/');

                    // Check for circular redirect
                    if (in_array($next_normalized, $chain)) {
                        $chain[] = $next_normalized . ' (circular)';
                        break;
                    }

                    $chain[] = $next;
                    $visited[$current] = true;
                    $current = $next_normalized;
                    $hop_count++;
                }

                // Only report chains with more than 1 hop (more than 2 URLs in chain)
                if (count($chain) > 2) {
                    $chains[] = [
                        'id' => md5(implode('|', $chain)),
                        'chain' => $chain,
                        'hops' => count($chain) - 1,
                        'source' => $chain[0],
                        'final_destination' => end($chain),
                        'suggestion' => sprintf(
/* translators: %1$: value, %2$: value */
                            __('Consolidate: %1$s → %2$s', 'prorank-seo'),
                            $chain[0],
                            end($chain)
                        ),
                    ];
                }
            }

            // Sort by number of hops (longest chains first)
            usort($chains, function($a, $b) {
                return $b['hops'] - $a['hops'];
            });

            return rest_ensure_response([
                'success' => true,
                'chains' => $chains,
                'total' => count($chains),
                'message' => count($chains) > 0
/* translators: %1$: value */
                    ? sprintf(__('Found %1$d redirect chain(s) that can be optimized.', 'prorank-seo'), count($chains))
                    : __('No redirect chains detected. Your redirects are optimized!', 'prorank-seo'),
            ]);
        } catch (\Exception $e) {
            return new WP_Error(
                'prorank_seo_rest_error',
                $e->getMessage(),
                ['status' => 500]
            );
        }
    }

    /**
     * Get default settings
     *
     * @return array
     */
    private function get_default_settings(): array {
        return [
            'strip_category_base' => false,
            'strip_tag_base' => false,
            'remove_replytocom' => false,
            'remove_utm_parameters' => false,
            'utm_to_fragment' => false,
            'force_trailing_slash' => false,
            'remove_trailing_slash' => false,
            'normalize_internal_archive_links' => false,
            'author_date_archive_link_policy' => 'default',
            'convert_to_lowercase' => false,
            'remove_stop_words' => false,
            'attachment_redirect' => 'off',
            'author_archive_handling' => 'default',
            'date_archive_handling' => 'default',
            'remove_query_strings' => false,
            'remove_feed_links' => false,
            'remove_shortlink' => false,
            'remove_rest_api_links' => false,
            'clean_permalinks' => false,
            'param_whitelist' => 'gclid,fbclid,msclkid,_ga',
        ];
    }

    /**
     * Load URL cleanup settings from grouped storage with legacy-option fallback.
     *
     * @return array
     */
    private function get_persisted_settings(): array {
        $defaults = $this->get_default_settings();
        $grouped_settings = [];

        try {
            $grouped_settings = Plugin::get_instance()->settings()->get_settings('url_cleanup');
        } catch (\Throwable $e) {
            $grouped_settings = [];
        }

        if (!is_array($grouped_settings)) {
            $grouped_settings = [];
        }

        $settings = array_merge($defaults, array_intersect_key($grouped_settings, $defaults));

        foreach ($defaults as $key => $default) {
            $settings[$key] = get_option('prorank_seo_url_cleanup_' . $key, $settings[$key] ?? $default);
        }

        return $settings;
    }
    
    /**
     * Validate settings
     *
     * @param array $settings Settings to validate
     * @return array Validated settings
     */
    private function validate_settings(array $settings): array {
        $defaults = $this->get_default_settings();
        $validated = [];

        // Validate boolean settings
        foreach ($defaults as $key => $default_value) {
            if (is_bool($default_value)) {
                $validated[$key] = isset($settings[$key]) ? (bool) $settings[$key] : $default_value;
            }
        }

        // Validate attachment_redirect
        $valid_attachment_options = ['off', 'parent', 'file'];
        $validated['attachment_redirect'] = isset($settings['attachment_redirect']) && in_array($settings['attachment_redirect'], $valid_attachment_options)
            ? $settings['attachment_redirect']
            : 'off';

        // Validate archive handling settings
        $valid_archive_options = ['default', 'redirect_home', 'disable_404', 'noindex'];
        $valid_archive_link_policies = ['default', 'nofollow', 'remove'];

        $validated['author_archive_handling'] = isset($settings['author_archive_handling']) && in_array($settings['author_archive_handling'], $valid_archive_options)
            ? $settings['author_archive_handling']
            : 'default';

        $validated['date_archive_handling'] = isset($settings['date_archive_handling']) && in_array($settings['date_archive_handling'], $valid_archive_options)
            ? $settings['date_archive_handling']
            : 'default';

        $validated['author_date_archive_link_policy'] = isset($settings['author_date_archive_link_policy']) && in_array($settings['author_date_archive_link_policy'], $valid_archive_link_policies, true)
            ? $settings['author_date_archive_link_policy']
            : 'default';

        // Ensure trailing slash settings are mutually exclusive
        if ($validated['force_trailing_slash'] && $validated['remove_trailing_slash']) {
            $validated['remove_trailing_slash'] = false;
        }

        // Validate param_whitelist (comma-separated string)
        $validated['param_whitelist'] = isset($settings['param_whitelist'])
            ? sanitize_text_field($settings['param_whitelist'])
            : 'gclid,fbclid,msclkid,_ga';

        return $validated;
    }
    
    /**
     * Build URL from parsed components
     *
     * @param array $parsed Parsed URL components
     * @return string
     */
    private function build_url(array $parsed): string {
        $url = '';
        
        if (isset($parsed['scheme'])) {
            $url .= $parsed['scheme'] . '://';
        }
        
        if (isset($parsed['host'])) {
            $url .= $parsed['host'];
        }
        
        if (isset($parsed['port'])) {
            $url .= ':' . $parsed['port'];
        }
        
        if (isset($parsed['path'])) {
            $url .= $parsed['path'];
        }
        
        if (isset($parsed['query']) && !empty($parsed['query'])) {
            $url .= '?' . $parsed['query'];
        }
        
        if (isset($parsed['fragment'])) {
            $url .= '#' . $parsed['fragment'];
        }
        
        return $url;
    }

    /**
     * Apply tracking-parameter cleanup rules to a parsed URL array.
     *
     * @param array $parsed
     * @param array $settings
     * @return array<int, string>
     */
    private function apply_tracking_parameter_cleanup(array &$parsed, array $settings): array {
        $rules_applied = [];

        if (empty($parsed['query']) || (empty($settings['remove_utm_parameters']) && empty($settings['utm_to_fragment']))) {
            return $rules_applied;
        }

        $query_params = [];
        wp_parse_str((string) $parsed['query'], $query_params);

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

        [$remaining_params, $removed_params] = $this->partition_tracking_query_params($query_params, $settings);

        if (empty($removed_params)) {
            return $rules_applied;
        }

        $parsed['query'] = empty($remaining_params) ? '' : http_build_query($remaining_params);

        if (!empty($settings['utm_to_fragment'])) {
            $fragment = http_build_query($removed_params);
            if ($fragment !== '') {
                $parsed['fragment'] = $fragment;
                $rules_applied[] = __('Moved tracking parameters to URL fragment', 'prorank-seo');
            }
        } else {
            $rules_applied[] = __('Removed tracking parameters', 'prorank-seo');
        }

        return $rules_applied;
    }

    /**
     * Split query parameters into remaining and removed tracking parameters.
     *
     * @param array $query_params
     * @param array $settings
     * @return array{0: array, 1: array}
     */
    private function partition_tracking_query_params(array $query_params, array $settings): array {
        $remaining = $query_params;
        $removed = [];
        $whitelist = $this->get_param_whitelist($settings);
        $tracking_keys = $this->get_tracking_param_names();

        foreach ($query_params as $key => $value) {
            $normalized_key = strtolower((string) $key);
            $is_tracking = str_starts_with($normalized_key, 'utm_') || in_array($normalized_key, $tracking_keys, true);

            if (!$is_tracking || in_array($normalized_key, $whitelist, true)) {
                continue;
            }

            $removed[$key] = $value;
            unset($remaining[$key]);
        }

        return [$remaining, $removed];
    }

    /**
     * Parse the parameter whitelist into normalized lowercase keys.
     *
     * @param array $settings
     * @return array<int, string>
     */
    private function get_param_whitelist(array $settings): array {
        $raw = (string) ($settings['param_whitelist'] ?? '');
        if ($raw === '') {
            return [];
        }

        $parts = array_map('trim', explode(',', $raw));
        $parts = array_filter($parts, static fn($part): bool => $part !== '');

        return array_values(array_unique(array_map('strtolower', $parts)));
    }

    /**
     * Get supported tracking parameter names in addition to the generic utm_* family.
     *
     * @return array<int, string>
     */
    private function get_tracking_param_names(): array {
        return [
            '_ga',
            'dclid',
            'fbclid',
            'gbraid',
            'gclid',
            'mc_cid',
            'mc_eid',
            'msclkid',
            'twclid',
            'wbraid',
            'yclid',
        ];
    }
    
    /**
     * Get settings args for REST API
     *
     * @return array
     */
    private function get_settings_args(): array {
        return [
            'strip_category_base' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'strip_tag_base' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_replytocom' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_utm_parameters' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'utm_to_fragment' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'force_trailing_slash' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_trailing_slash' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'normalize_internal_archive_links' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'author_date_archive_link_policy' => [
                'type' => 'string',
                'default' => 'default',
                'enum' => ['default', 'nofollow', 'remove'],
            ],
            'convert_to_lowercase' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_stop_words' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'attachment_redirect' => [
                'type' => 'string',
                'default' => 'off',
                'enum' => ['off', 'parent', 'file'],
            ],
            'author_archive_handling' => [
                'type' => 'string',
                'default' => 'default',
                'enum' => ['default', 'redirect_home', 'disable_404', 'noindex'],
            ],
            'date_archive_handling' => [
                'type' => 'string',
                'default' => 'default',
                'enum' => ['default', 'redirect_home', 'disable_404', 'noindex'],
            ],
            'remove_query_strings' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_feed_links' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_shortlink' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'remove_rest_api_links' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'clean_permalinks' => [
                'type' => 'boolean',
                'default' => false,
            ],
            'param_whitelist' => [
                'type' => 'string',
                'default' => 'gclid,fbclid,msclkid,_ga',
            ],
        ];
    }
}
