<?php

/**
 * External CSS Download Background Task
 *
 * Downloads and caches external CSS files in the background
 *
 * @package ProRank\SEO\Core\Tasks
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\Tasks;

use ProRank\SEO\Core\Optimization\CSS\ExternalAssetCacheService;

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


/**
 * ExternalCssDownloadTask class
 */


class ExternalCssDownloadTask {
    
    /**
     * Task hook name
     *
     * @var string
     */
    const HOOK_NAME = 'prorank_external_css_download';
    
    /**
     * Batch size for processing
     *
     * @var int
     */
    const BATCH_SIZE = 3;
    
    /**
     * External asset cache service
     *
     * @var ExternalAssetCacheService
     */
    private ExternalAssetCacheService $cache_service;
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->cache_service = new ExternalAssetCacheService();
    }
    
    /**
     * Initialize the task
     *
     * @return void
     */
    public function init(): void {
        // Register the action for Action Scheduler
        add_action(self::HOOK_NAME, [$this, 'process_batch'], 10, 1);
        add_action(self::HOOK_NAME . '_retry', [$this, 'process_batch'], 10, 1);
        
        // Hook into page parsing to detect external CSS
        add_action('wp_enqueue_scripts', [$this, 'detect_external_css'], 999);
    }
    
    /**
     * Process a batch of external CSS URLs
     *
     * @param array $args Task arguments
     * @return void
     */
    public function process_batch($args = []): void {
        if (!is_array($args)) {
            $args = [];
        }

        try {
            // Get unprocessed external CSS URLs
            $urls = $this->get_unprocessed_urls(self::BATCH_SIZE);
            
            if (empty($urls)) {
                // No more URLs to process
                $this->mark_download_complete();
                return;
            }
            
            foreach ($urls as $url_data) {
                $this->download_external_css($url_data);
            }
            
            // Schedule next batch
            $this->schedule_next_batch();
            
        } catch (\Exception $e) {
            // Log error
            prorank_log('ProRank External CSS Download Error: ' . $e->getMessage());
            
            // Retry in 5 minutes
            as_schedule_single_action(
                time() + 300,
                self::HOOK_NAME,
                $args,
                'prorank-seo'
            );
        }
    }
    
    /**
     * Download and cache external CSS
     *
     * @param array $url_data URL data
     * @return void
     */
    private function download_external_css(array $url_data): void {
        $url = $url_data['url'];
        
        try {
            // Use cache service to download and store
            $result = $this->cache_service->get($url);
            
            if ($result && !empty($result['content'])) {
                // Mark as successfully downloaded
                $this->mark_url_downloaded($url_data, $result);
                
                // Update statistics
                $this->update_download_statistics($url_data, $result);
            } else {
                throw new \Exception('Failed to download CSS content');
            }
            
        } catch (\Exception $e) {
            // Log error for this URL
            prorank_log("ProRank External CSS Download Error for {$url}: " . $e->getMessage());
            
            // Mark URL as failed
            $this->mark_url_failed($url_data, $e->getMessage());
        }
    }
    
    /**
     * Detect external CSS URLs on the page
     *
     * @return void
     */
    public function detect_external_css(): void {
        global $wp_styles;
        
        if (!$wp_styles || !is_object($wp_styles)) {
            return;
        }
        
        $external_urls = [];
        
        foreach ($wp_styles->registered as $handle => $style) {
            // Skip if no src or if src is not a string (WordPress sets src to true for inline styles)
            if (empty($style->src) || !is_string($style->src)) {
                continue;
            }

            // Check if it's an external URL
            if ($this->is_external_url($style->src)) {
                $external_urls[] = [
                    'url' => $style->src,
                    'handle' => $handle,
                    'version' => $style->ver ?: '',
                    'media' => $style->args ?: 'all',
                ];
            }
        }
        
        // Queue external URLs for download
        if (!empty($external_urls)) {
            $this->queue_urls($external_urls);
        }
    }
    
    /**
     * Check if URL is external
     *
     * @param string $url URL to check
     * @return bool
     */
    private function is_external_url(string $url): bool {
        // Parse URLs
        $site_host = wp_parse_url(site_url(), PHP_URL_HOST);
        $url_host = wp_parse_url($url, PHP_URL_HOST);
        
        // If no host in URL, it's relative
        if (!$url_host) {
            return false;
        }
        
        // Compare hosts
        return $url_host !== $site_host;
    }
    
    /**
     * Get unprocessed external CSS URLs
     *
     * @param int $limit Number of URLs to get
     * @return array
     */
    private function get_unprocessed_urls(int $limit): array {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_external_css_queue';
        
        // Create table if not exists
        $this->ensure_table_exists();
        
        $results = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table} 
             WHERE status = 'pending' 
             ORDER BY priority DESC, created_at ASC 
             LIMIT %d",
            $limit
        ), ARRAY_A);
        
        return $results ?: [];
    }
    
    /**
     * Mark URL as downloaded
     *
     * @param array $url_data URL data
     * @param array $result Download result
     * @return void
     */
    private function mark_url_downloaded(array $url_data, array $result): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_external_css_queue';
        
        $wpdb->update(
            $table,
            [
                'status' => 'downloaded',
                'local_path' => $result['local_url'],
                'file_size' => strlen($result['content']),
                'processed_at' => current_time('mysql'),
            ],
            ['id' => $url_data['id']],
            ['%s', '%s', '%d', '%s'],
            ['%d']
        );
    }
    
    /**
     * Mark URL as failed
     *
     * @param array $url_data URL data
     * @param string $error Error message
     * @return void
     */
    private function mark_url_failed(array $url_data, string $error): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_external_css_queue';
        
        $wpdb->update(
            $table,
            [
                'status' => 'failed',
                'error_message' => $error,
                'retry_count' => $url_data['retry_count'] + 1,
                'processed_at' => current_time('mysql'),
            ],
            ['id' => $url_data['id']],
            ['%s', '%s', '%d', '%s'],
            ['%d']
        );
        
        // Schedule retry if under limit
        if ($url_data['retry_count'] < 3) {
            $this->schedule_retry($url_data);
        }
    }
    
    /**
     * Update download statistics
     *
     * @param array $url_data URL data
     * @param array $result Download result
     * @return void
     */
    private function update_download_statistics(array $url_data, array $result): void {
        $stats = get_option('prorank_external_css_stats', [
            'total_downloads' => 0,
            'total_size_saved' => 0,
            'total_bandwidth_saved' => 0,
        ]);
        
        $stats['total_downloads']++;
        $stats['total_size_saved'] += strlen($result['content']);
        
        // Estimate bandwidth saved (assume each file would be downloaded 100 times)
        $stats['total_bandwidth_saved'] += strlen($result['content']) * 100;
        
        update_option('prorank_external_css_stats', $stats);
    }
    
    /**
     * Schedule next batch
     *
     * @return void
     */
    private function schedule_next_batch(): void {
        as_schedule_single_action(
            time() + 30, // Process next batch in 30 seconds
            self::HOOK_NAME,
            [],
            'prorank-seo'
        );
    }
    
    /**
     * Schedule retry for failed URL
     *
     * @param array $url_data URL data
     * @return void
     */
    private function schedule_retry(array $url_data): void {
        // Exponential backoff: 5 min, 15 min, 45 min
        $delay = 300 * pow(3, $url_data['retry_count']);
        
        as_schedule_single_action(
            time() + $delay,
            self::HOOK_NAME . '_retry',
            ['url_id' => $url_data['id']],
            'prorank-seo'
        );
    }
    
    /**
     * Mark download process as complete
     *
     * @return void
     */
    private function mark_download_complete(): void {
        update_option('prorank_external_css_download_complete', true);
        update_option('prorank_external_css_download_timestamp', time());
        
        // Trigger completion action
        do_action('prorank_external_css_download_complete');
    }
    
    /**
     * Ensure database table exists
     *
     * @return void
     */
    private function ensure_table_exists(): void {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'prorank_external_css_queue';
        $charset_collate = $wpdb->get_charset_collate();
        
        $sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            url varchar(500) NOT NULL,
            handle varchar(100) DEFAULT '',
            version varchar(50) DEFAULT '',
            media varchar(50) DEFAULT 'all',
            status varchar(20) DEFAULT 'pending',
            priority int(11) DEFAULT 0,
            local_path varchar(255) DEFAULT NULL,
            file_size bigint(20) UNSIGNED DEFAULT 0,
            retry_count int(11) DEFAULT 0,
            error_message text,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            processed_at datetime DEFAULT NULL,
            PRIMARY KEY (id),
            UNIQUE KEY url (url),
            KEY status (status),
            KEY priority (priority)
        ) {$charset_collate};";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    /**
     * Queue external CSS URLs for download
     *
     * @param array $urls Array of URL data
     * @return void
     */
    public function queue_urls(array $urls): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_external_css_queue';
        $this->ensure_table_exists();
        
        foreach ($urls as $url_data) {
            // Check if URL is allowed
            if (!$this->is_url_allowed($url_data['url'])) {
                continue;
            }
            
            // Insert or update
            $wpdb->replace(
                $table,
                [
                    'url' => $url_data['url'],
                    'handle' => $url_data['handle'] ?? '',
                    'version' => $url_data['version'] ?? '',
                    'media' => $url_data['media'] ?? 'all',
                    'status' => 'pending',
                    'priority' => $this->calculate_priority($url_data),
                    'created_at' => current_time('mysql'),
                ],
                ['%s', '%s', '%s', '%s', '%s', '%d', '%s']
            );
        }
        
        // Trigger processing
        if (!as_next_scheduled_action(self::HOOK_NAME)) {
            $this->schedule_next_batch();
        }
    }
    
    /**
     * Check if URL is allowed for caching
     *
     * @param string $url URL to check
     * @return bool
     */
    private function is_url_allowed(string $url): bool {
        // Only allow Google Fonts CSS for local hosting in this build.
        $allowed_hosts = [
            'fonts.googleapis.com',
        ];
        
        $url_host = wp_parse_url($url, PHP_URL_HOST);
        
        return in_array($url_host, $allowed_hosts, true);
    }
    
    /**
     * Calculate priority for URL
     *
     * @param array $url_data URL data
     * @return int
     */
    private function calculate_priority(array $url_data): int {
        $priority = 0;
        
        // Google Fonts get highest priority
        if (strpos($url_data['url'], 'fonts.googleapis.com') !== false) {
            $priority = 100;
        }
        return $priority;
    }
}
