<?php

/**
 * Click Tracker
 *
 * Tracks clicks on internal links for analytics
 *
 * @package ProRank\SEO\Modules\Content\InternalLinking\Tracking
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Modules\Content\InternalLinking\Tracking;

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


/**
 * ClickTracker class
 */


class ClickTracker {
    /**
     * Resolved referrer column name for click table.
     *
     * @var string|null
     */
    private ?string $referrer_column = null;

    /**
     * Tracks whether the referrer column has been resolved.
     *
     * @var bool
     */
    private bool $referrer_column_resolved = false;
    
    /**
     * Initialize click tracking
     *
     * @return void
     */
    public function init(): void {
        // Add AJAX handlers for click tracking
        add_action('wp_ajax_prorank_track_link_click', [$this, 'track_click']);
        add_action('wp_ajax_nopriv_prorank_track_link_click', [$this, 'track_click']);
        
        // Add data attributes to links
        add_filter('the_content', [$this, 'add_tracking_attributes'], 20);
        
        // Enqueue tracking script
        add_action('wp_enqueue_scripts', [$this, 'enqueue_tracking_script']);

        // Schedule daily cleanup of old click data.
        add_action('prorank_link_clicks_cleanup', [$this, 'scheduled_cleanup']);
        if (!wp_next_scheduled('prorank_link_clicks_cleanup')) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'prorank_link_clicks_cleanup');
        }
    }
    
    /**
     * Track link click via AJAX
     *
     * @return void
     */
    public function track_click(): void {
        // Verify nonce
        $nonce = (string) prorank_get_input_value($_POST, 'nonce', '');
        if ($nonce === '' || !wp_verify_nonce($nonce, 'prorank_link_tracking')) {
            wp_die('Invalid nonce');
        }
        
        // Get click data
        $source_url = sanitize_url((string) prorank_get_input_value($_POST, 'source_url', ''));
        $target_url = sanitize_url((string) prorank_get_input_value($_POST, 'target_url', ''));
        $link_id = absint(prorank_get_post_var('link_id'));
        $anchor_text = prorank_get_post_var('anchor_text');
        $position = intval(prorank_get_post_var('position'));
        $is_autolinked = prorank_get_post_var('is_autolinked') === 'true';
        
        // Get post IDs from URLs
        $source_post_id = url_to_postid($source_url);
        $target_post_id = url_to_postid($target_url);
        
        if (!$source_post_id || !$target_post_id) {
            wp_send_json_error('Invalid URLs');
            return;
        }
        
        // Get user data
        $user_id = get_current_user_id();
        $user_ip = $this->get_client_ip();
        $user_agent = prorank_get_server_var('HTTP_USER_AGENT');
        
        // Store click data
        global $wpdb;
        $table = $wpdb->prefix . 'prorank_link_clicks';

        $insert_data = [
            'link_id' => $link_id,
            'source_post_id' => $source_post_id,
            'target_post_id' => $target_post_id,
            'target_url' => $target_url,
            'anchor_text' => $anchor_text,
            'click_position' => $position,
            'is_autolinked' => $is_autolinked ? 1 : 0,
            'user_id' => $user_id,
            'user_ip' => $user_ip,
            'user_agent' => $user_agent,
            'clicked_at' => current_time('mysql'),
        ];

        $formats = [
            '%d', '%d', '%d', '%s', '%s', '%d', '%d',
            '%d', '%s', '%s', '%s'
        ];

        $referrer_column = $this->resolve_referrer_column($table);
        if ($referrer_column !== null) {
            $insert_data[$referrer_column] = prorank_get_server_var('HTTP_REFERER');
            $formats[] = '%s';
        }
        
        $result = $wpdb->insert(
            $table,
            $insert_data,
            $formats
        );
        
        if ($result) {
            // Update click count in link suggestions
            $this->update_suggestion_stats($source_post_id, $target_post_id);
            
            wp_send_json_success([
                'message' => 'Click tracked successfully',
                'click_id' => $wpdb->insert_id,
            ]);
        } else {
            wp_send_json_error('Failed to track click');
        }
    }
    
    /**
     * Add tracking attributes to links
     *
     * @param string $content Post content
     * @return string Modified content
     */
    public function add_tracking_attributes(string $content): string {
        // Only add tracking on frontend
        if (is_admin()) {
            return $content;
        }
        
        // Get current post ID
        $post_id = get_the_ID();
        if (!$post_id) {
            return $content;
        }
        
        // Parse HTML
        $dom = new \DOMDocument();
        $dom->encoding = 'UTF-8';
        
        // Suppress warnings for HTML5 tags
        libxml_use_internal_errors(true);
        $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
        libxml_clear_errors();
        
        $xpath = new \DOMXPath($dom);
        $links = $xpath->query('//a[@href]');
        
        $position = 0;
        foreach ($links as $link) {
            $href = $link->getAttribute('href');
            
            // Check if internal link
            if (!$this->is_internal_link($href)) {
                continue;
            }
            
            // Add tracking attributes
            $link->setAttribute('data-prorank-track', 'true');
            $link->setAttribute('data-link-position', (string) $position);
            
            // Check if auto-linked
            if ($link->hasAttribute('data-autolinked')) {
                $link->setAttribute('data-is-autolinked', 'true');
            }
            
            $position++;
        }
        
        // Return modified content
        $body = $dom->getElementsByTagName('body')->item(0);
        if ($body) {
            $html = '';
            foreach ($body->childNodes as $child) {
                $html .= $dom->saveHTML($child);
            }
            return $html;
        }
        
        return $content;
    }
    
    /**
     * Enqueue tracking script
     *
     * @return void
     */
    public function enqueue_tracking_script(): void {
        if (is_admin()) {
            return;
        }
        
        wp_enqueue_script(
            'prorank-link-tracking',
            PRORANK_SEO_URL . 'assets/js/link-tracking.js',
            ['jquery'],
            PRORANK_SEO_VERSION,
            true
        );
        
        wp_localize_script('prorank-link-tracking', 'prorankLinkTracking', [
            'ajaxurl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('prorank_link_tracking'),
            'track_external' => apply_filters('prorank_track_external_links', false),
        ]);
    }
    
    /**
     * Get click statistics for a post
     *
     * @param int $post_id Post ID
     * @param int $days    Number of days to look back
     * @return array Statistics
     */
    public function get_click_stats(int $post_id, int $days = 30): array {
        global $wpdb;
        $table = $wpdb->prefix . 'prorank_link_clicks';
        
        $date_limit = wp_date('Y-m-d H:i:s', strtotime("-{$days} days"));
        
        // Get outbound clicks (from this post)
        $outbound = $wpdb->get_results($wpdb->prepare(
            "SELECT 
                target_post_id,
                anchor_text,
                COUNT(*) as click_count,
                MAX(clicked_at) as last_clicked
            FROM $table
            WHERE source_post_id = %d
            AND clicked_at > %s
            GROUP BY target_post_id, anchor_text
            ORDER BY click_count DESC",
            $post_id,
            $date_limit
        ), ARRAY_A);
        
        // Get inbound clicks (to this post)
        $inbound = $wpdb->get_results($wpdb->prepare(
            "SELECT 
                source_post_id,
                anchor_text,
                COUNT(*) as click_count,
                MAX(clicked_at) as last_clicked
            FROM $table
            WHERE target_post_id = %d
            AND clicked_at > %s
            GROUP BY source_post_id, anchor_text
            ORDER BY click_count DESC",
            $post_id,
            $date_limit
        ), ARRAY_A);
        
        // Get total clicks
        $total_outbound = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table 
            WHERE source_post_id = %d AND clicked_at > %s",
            $post_id,
            $date_limit
        ));
        
        $total_inbound = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $table 
            WHERE target_post_id = %d AND clicked_at > %s",
            $post_id,
            $date_limit
        ));
        
        return [
            'outbound' => [
                'total' => (int) $total_outbound,
                'links' => $outbound,
            ],
            'inbound' => [
                'total' => (int) $total_inbound,
                'links' => $inbound,
            ],
            'period_days' => $days,
        ];
    }
    
    /**
     * Get top clicked links
     *
     * @param int $limit Limit
     * @param int $days  Days to look back
     * @return array Top links
     */
    public function get_top_clicked_links(int $limit = 10, int $days = 30): array {
        global $wpdb;
        $table = $wpdb->prefix . 'prorank_link_clicks';
        
        $date_limit = wp_date('Y-m-d H:i:s', strtotime("-{$days} days"));
        
        return $wpdb->get_results($wpdb->prepare(
            "SELECT 
                source_post_id,
                target_post_id,
                anchor_text,
                COUNT(*) as click_count,
                MAX(clicked_at) as last_clicked
            FROM $table
            WHERE clicked_at > %s
            GROUP BY source_post_id, target_post_id, anchor_text
            ORDER BY click_count DESC
            LIMIT %d",
            $date_limit,
            $limit
        ), ARRAY_A);
    }
    
    /**
     * Check if URL is internal
     *
     * @param string $url URL to check
     * @return bool
     */
    private function is_internal_link(string $url): bool {
        // Empty or anchor links
        if (empty($url) || $url[0] === '#') {
            return false;
        }
        
        // Parse URLs
        $site_url = wp_parse_url(home_url());
        $link_url = wp_parse_url($url);
        
        // Relative URLs are internal
        if (!isset($link_url['host'])) {
            return true;
        }
        
        // Compare hosts
        return $link_url['host'] === $site_url['host'];
    }
    
    /**
     * Get client IP address
     *
     * @return string
     */
    private function get_client_ip(): string {
        $ip_keys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];
        
        foreach ($ip_keys as $key) {
            $server_value = prorank_get_server_var($key);
            if ($server_value !== '') {
                $ip_list = explode(',', $server_value);
                foreach ($ip_list as $ip) {
                    $ip = trim($ip);
                    
                    if (filter_var(
                        $ip,
                        FILTER_VALIDATE_IP,
                        FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
                    ) !== false) {
                        return $ip;
                    }
                }
            }
        }
        
        return prorank_get_server_var('REMOTE_ADDR') ?: '0.0.0.0';
    }
    
    /**
     * Update suggestion statistics
     *
     * @param int $source_post_id Source post ID
     * @param int $target_post_id Target post ID
     * @return void
     */
    private function update_suggestion_stats(int $source_post_id, int $target_post_id): void {
        // Update click count in post meta
        $meta_key = '_prorank_link_clicks_' . $target_post_id;
        $current_count = (int) get_post_meta($source_post_id, $meta_key, true);
        update_post_meta($source_post_id, $meta_key, $current_count + 1);
        
        // Trigger action for other plugins
        do_action('prorank_link_clicked', $source_post_id, $target_post_id);
    }

    /**
     * Cron callback: clean old click data using configurable retention.
     *
     * @return void
     */
    public function scheduled_cleanup(): void {
        $days = (int) get_option('prorank_link_clicks_retention_days', 90);
        $deleted = $this->clean_old_data($days);

        if ($deleted > 0) {
            prorank_log("[ProRank SEO] Link clicks cleanup: deleted {$deleted} rows older than {$days} days.");
        }
    }
    
    /**
     * Clean old click data
     *
     * @param int $days Days to keep
     * @return int Number of rows deleted
     */
    public function clean_old_data(int $days = 90): int {
        global $wpdb;
        $table = $wpdb->prefix . 'prorank_link_clicks';
        
        $date_limit = wp_date('Y-m-d H:i:s', strtotime("-{$days} days"));
        
        return $wpdb->query($wpdb->prepare(
            "DELETE FROM $table WHERE clicked_at < %s",
            $date_limit
        ));
    }

    /**
     * Resolve compatible referrer column name (legacy spellings supported).
     *
     * @param string $table Click table name.
     * @return string|null
     */
    private function resolve_referrer_column(string $table): ?string {
        if ($this->referrer_column_resolved) {
            return $this->referrer_column;
        }

        $this->referrer_column_resolved = true;

        global $wpdb;
        foreach (['referer', 'referrer'] as $column) {
            $exists = $wpdb->get_var(
                $wpdb->prepare(
                    "SHOW COLUMNS FROM {$table} LIKE %s",
                    $column
                )
            );

            if (!empty($exists)) {
                $this->referrer_column = $column;
                return $this->referrer_column;
            }
        }

        $this->referrer_column = null;
        return null;
    }
}
