<?php

namespace LinkFiliate\Libs\Utils;

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

use LinkFiliate\App\Traits\Singleton;
use LinkFiliate\App\Models\Campaigns;
use LinkFiliate\App\Models\Clicks;

/**
 * Redirect Handler for Affiliate Links
 * 
 * Handles redirect URLs in format: site.com/go/{campaign_id} or site.com/go/{slug}
 * 
 * @since 1.0.0
 */
class Redirect
{
    use Singleton;

    private $current_slug = '';
    private $target_slug = 'go';
    private $query_params = [];
    private $settings = [];
    private $campaign_cache = null; // Cache to avoid duplicate DB queries

    private function __construct()
    {
        $this->load_settings();

        add_action('init', [$this, 'capture_request'], 1);

        // Use 'parse_request' hook to intercept before WordPress query
        add_action('parse_request', [$this, 'check_campaign_match'], 1);

        add_action('template_redirect', [$this, 'handle_redirect'], 1);
    }


    /**
     * Load redirect settings from DB
     */
    private function load_settings()
    {
        global $wpdb;

        $table = $this->tables['settings'] ?? $wpdb->prefix . 'linkfiliate_settings';
        $data = $wpdb->get_var(
            $wpdb->prepare("SELECT option_value FROM %i WHERE option_key = %s", $table, 'general')
        );

        $defaults = [
            'customPrefix'           => 'go',
            'disablePrefixByDefault' => false,
            'caseSensitiveRedirects' => false,
            'globalNoFollow'         => false,
            'globalSponsored'        => false,
            'globalRedirectType'     => '302',
        ];

        if ($data) {
            $decoded = json_decode($data, true);
            if (is_array($decoded)) {
                $this->settings = array_merge($defaults, $decoded);
            } else {
                $this->settings = $defaults;
            }
        } else {
            $this->settings = $defaults;
        }

        // Apply prefix
        $this->target_slug = sanitize_title($this->settings['customPrefix']);
    }

    /**
     * Capture the incoming request details
     */
    public function capture_request()
    {
        if (isset($_SERVER['REQUEST_URI'])) {
            $request_uri = sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI']));
            $parsed = wp_parse_url($request_uri);
            $this->current_slug = isset($parsed['path']) ? trim($parsed['path'], '/') : '';

            $allowed_params = ['utm_source', 'utm_medium', 'utm_campaign','utm_content','utm_term'];
            $this->query_params = [];

            foreach ($allowed_params as $key) {
                if (isset($_GET[$key])) {
                    $this->query_params[$key] = sanitize_text_field(wp_unslash($_GET[$key]));
                }
            }
        }
    }

    /**
     * Check if URL matches a campaign (runs before WordPress query)
     * This prevents conflicts with WordPress posts/pages/CPTs
     */
    public function check_campaign_match($wp)
    {
        // Only process GET requests
        if (empty($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'GET') {
            return;
        }

        $path_parts = explode('/', $this->current_slug);
        $campaign_identifier = '';

        // Determine if this could be a campaign redirect
        if ($this->settings['disablePrefixByDefault']) {
            // No prefix mode: check first path segment
            $campaign_identifier = isset($path_parts[0]) ? sanitize_text_field($path_parts[0]) : '';

            // Skip if empty or looks like common WordPress paths
            if (empty($campaign_identifier) || $this->is_wordpress_reserved($campaign_identifier)) {
                return;
            }
        } else {
            // Prefix mode: check if starts with target_slug
            if (!isset($path_parts[0]) || $path_parts[0] !== $this->target_slug) {
                return;
            }
            $campaign_identifier = isset($path_parts[1]) ? sanitize_text_field($path_parts[1]) : '';
        }

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

        // Try to find campaign
        $campaign = $this->find_campaign($campaign_identifier);

        if ($campaign) {
            // Campaign found! Cache it and stop WordPress from processing
            $this->campaign_cache = $campaign;

            // Stop WordPress query processing
            $wp->query_vars = [];
            add_filter('do_parse_request', '__return_false');
        }
    }

    /**
     * Handle the redirect logic
     */
    public function handle_redirect()
    {
        // Only process if we have a cached campaign from check_campaign_match
        if (!$this->campaign_cache) {
            return;
        }

        $campaign = $this->campaign_cache;

        // Get campaign settings
        $settings = isset($campaign['settings']) && is_array($campaign['settings'])
            ? $campaign['settings']
            : [];

        // Check if tracking is enabled for this campaign
        $tracking_enabled = isset($settings['tracking']) ? (bool)$settings['tracking'] : true;

        // Track the click only if tracking is enabled
        if ($tracking_enabled) {
            $this->track_click($campaign);
        }

        // Get target URL and redirect
        $target_url = $this->build_target_url($campaign);

        if (empty($target_url)) {
            $this->send_404('Invalid redirect URL');
            return;
        }

        // Perform the redirect
        $this->perform_redirect($target_url, $campaign);
    }

    /**
     * Check if slug is a WordPress reserved path
     * 
     * @param string $slug The slug to check
     * @return bool True if reserved
     */
    private function is_wordpress_reserved($slug)
    {
        // Common WordPress reserved paths
        $reserved = [
            'wp-admin',
            'wp-content',
            'wp-includes',
            'wp-json',
            'feed',
            'rss',
            'rss2',
            'rdf',
            'atom',
            'embed',
            'page',
            'paged',
            'xmlrpc.php',
            'wp-login.php',
        ];

        return in_array($slug, $reserved, true);
    }

    /**
     * Find campaign by ID or slug (without dying)
     * 
     * @param string $identifier Campaign ID or slug
     * @return array|null Campaign data or null if not found
     */
    private function find_campaign($identifier)
    {
        if (empty($identifier)) {
            return null;
        }

        $campaignModel = new Campaigns();

        // Try to find by ID first (if numeric)
        if (is_numeric($identifier)) {
            $campaign = $campaignModel->find(absint($identifier));
            if ($campaign) {
                return $campaign;
            }
        }

        // Try to find by slug
        $slug = sanitize_title($identifier);

        // Apply case sensitivity setting
        if (!$this->settings['caseSensitiveRedirects']) {
            // Case-insensitive search might require custom method in Campaigns model
            $campaign = $campaignModel->find_by_slug($slug);
        } else {
            $campaign = $campaignModel->find_by_slug($slug);
        }

        return $campaign;
    }

    /**
     * Get campaign by ID or slug (legacy method with wp_die)
     * 
     * @param string $identifier Campaign ID or slug
     * @return array|null Campaign data or null if not found
     */
    private function get_campaign($identifier)
    {
        $campaign = $this->find_campaign($identifier);

        if (!$campaign) {
            wp_die(
                esc_html__('Campaign not found.', 'linkfiliate'),
                esc_html__('Not Found', 'linkfiliate'),
                [
                    'response' => 404,
                    'back_link' => true,
                ]
            );
        }

        return $campaign;
    }

    /**
     * Track the click in database
     * 
     * @param array $campaign Campaign data
     */
    private function track_click($campaign)
    {
        try {
            $clickModel = new Clicks();

            // Get tracking data
            $tracking_data = $this->get_tracking_data();

            // Generate unique click ID
            $click_id = 'Lf' . bin2hex(random_bytes(6));

            // Save click record
            $clickModel->create([
                'id'            => $click_id,
                'campaign_id'   => $campaign['id'],
                'ip_address'    => $tracking_data['ip_address'],
                'user_agent'    => $tracking_data['user_agent'],
                'created_at'    => current_time('mysql'),
            ]);

            // Set cookie with ref_id for attribution
            $this->set_ref_cookie($click_id);
        } catch (\Exception $e) {
            // Log error but don't stop redirect
            // error_log('LinkFiliate Click Tracking Error: ' . $e->getMessage());
        }
    }

    /**
     * Get tracking data from request
     * 
     * @return array Tracking data
     */
    private function get_tracking_data()
    {
        // Get UTM parameters from URL
        $utm_source   = $this->query_params['utm_source'] ?? '';
        $utm_medium   = $this->query_params['utm_medium'] ?? '';
        $utm_campaign = $this->query_params['utm_campaign'] ?? '';
        $utm_term     = $this->query_params['utm_term'] ?? '';
        $utm_content  = $this->query_params['utm_content'] ?? '';
        $utm_id       = $this->query_params['utm_id'] ?? '';

        // Get system data
        $ip_address = $this->get_client_ip();
        $user_agent = isset($_SERVER['HTTP_USER_AGENT'])
            ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))
            : '';
        $referer = isset($_SERVER['HTTP_REFERER'])
            ? esc_url_raw(wp_unslash($_SERVER['HTTP_REFERER']))
            : '';

        return [
            'utm_source'   => $utm_source,
            'utm_medium'   => $utm_medium,
            'utm_campaign' => $utm_campaign,
            'utm_term'     => $utm_term,
            'utm_content'  => $utm_content,
            'utm_id'       => $utm_id,
            'ip_address'   => $ip_address,
            'user_agent'   => $user_agent,
            'referer'      => $referer,
        ];
    }

    /**
     * Get real client IP address
     * 
     * @return string IP address
     */
    private function get_client_ip()
    {
        $ip_keys = [
            'HTTP_CF_CONNECTING_IP', // CloudFlare
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_REAL_IP',
            'REMOTE_ADDR'
        ];

        foreach ($ip_keys as $key) {
            if (isset($_SERVER[$key])) {
                $ip = sanitize_text_field(wp_unslash($_SERVER[$key]));
                // Handle multiple IPs (take first one)
                if (strpos($ip, ',') !== false) {
                    $ips = explode(',', $ip);
                    $ip = trim($ips[0]);
                }
                // Validate IP
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    return $ip;
                }
            }
        }

        return '0.0.0.0';
    }

    /**
     * Build target URL with parameters
     * 
     * @param array $campaign Campaign data
     * @return string Target URL
     */
    private function build_target_url($campaign)
    {
        $target_url = $campaign['target_url'] ?? '';

        if (empty($target_url)) {
            return '';
        }

        // Validate URL
        if (!filter_var($target_url, FILTER_VALIDATE_URL)) {
            return '';
        }
        $settings = isset($campaign['settings']) && is_array($campaign['settings'])
            ? $campaign['settings']
            : [];
        // Check if campaign should pass through UTM parameters
        $pass_params = isset($settings['parameter_forwarding']) && $settings['parameter_forwarding'];

        if ($pass_params && !empty($this->query_params)) {
            // Parse existing URL parameters
            $parsed = wp_parse_url($target_url);
            $existing_params = [];

            if (isset($parsed['query'])) {
                parse_str($parsed['query'], $existing_params);
            }

            // Merge with new parameters (new parameters don't override existing ones)
            $params = array_merge($this->query_params, $existing_params);

            // Rebuild URL
            $base_url = $parsed['scheme'] . '://' . $parsed['host'];
            if (isset($parsed['path'])) {
                $base_url .= $parsed['path'];
            }

            $target_url = add_query_arg($params, $base_url);
        }

        return esc_url_raw($target_url);
    }

    /**
     * Set referral cookie
     * 
     * @param string $ref_id Reference ID
     */
    private function set_ref_cookie($ref_id)
    {
        $expiry = time() + (30 * DAY_IN_SECONDS); // 30 days

        setcookie(
            'lf_ref',
            $ref_id,
            $expiry,
            COOKIEPATH,
            COOKIE_DOMAIN,
            is_ssl(),
            true // httponly
        );
    }

    /**
     * Perform the actual redirect
     * 
     * @param string $url Target URL
     * @param array $campaign Campaign data
     */
    private function perform_redirect($url, $campaign = [])
    {
        // Get redirect type from campaign settings (default to 302)
        $redirect_type = isset($campaign['redirect_type']) ? absint($campaign['redirect_type']) : 302;

        // Validate redirect type (only allow 301, 302, 307, 308)
        $allowed_types = [301, 302, 307, 308];
        if (!in_array($redirect_type, $allowed_types, true)) {
            $redirect_type = 302; // Fallback to 302 if invalid
        }

        wp_redirect($url, $redirect_type);
        exit;
    }

    /**
     * Send 404 response
     * 
     * @param string $message Error message (for logging)
     */
    private function send_404($message = '')
    {
        if (!empty($message)) {
            // error_log('LinkFiliate Redirect Error: ' . $message);
        }

        global $wp_query;
        $wp_query->set_404();
        status_header(404);
        get_template_part(404);
        exit;
    }
}
