<?php

/**
 * CSS Cache Generation Background Task
 *
 * Processes CSS files in the background for optimization and caching
 *
 * @package ProRank\SEO\Core\Tasks
 * @since   1.0.0
 */

declare(strict_types=1);

namespace ProRank\SEO\Core\Tasks;

use ProRank\SEO\Core\Optimization\CSS\CssMinifierService;
use ProRank\SEO\Core\Optimization\CSS\CssImportProcessorService;
use ProRank\SEO\Core\Optimization\CSS\CssPathRewriterService;

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


/**
 * CssCacheGenerationTask class
 */


class CssCacheGenerationTask {
    
    /**
     * Task hook name
     *
     * @var string
     */
    const HOOK_NAME = 'prorank_css_cache_generation';
    
    /**
     * Batch size for processing
     *
     * @var int
     */
    const BATCH_SIZE = 5;
    
    /**
     * CSS minifier service
     *
     * @var CssMinifierService
     */
    private CssMinifierService $minifier;
    
    /**
     * Import processor service
     *
     * @var CssImportProcessorService
     */
    private CssImportProcessorService $import_processor;
    
    /**
     * Path rewriter service
     *
     * @var CssPathRewriterService
     */
    private CssPathRewriterService $path_rewriter;
    
    /**
     * Constructor
     */
    public function __construct() {
        $this->minifier = new CssMinifierService();
        $this->path_rewriter = new CssPathRewriterService();
        $this->import_processor = new CssImportProcessorService($this->path_rewriter);
    }
    
    /**
     * 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);
        
        // Schedule initial task if not scheduled
        if (!as_next_scheduled_action(self::HOOK_NAME)) {
            $this->schedule_next_batch();
        }
    }
    
    /**
     * Process a batch of CSS files
     *
     * @param array $args Task arguments
     * @return void
     */
    public function process_batch(array $args = []): void {
        try {
            // Get unprocessed CSS files
            $css_files = $this->get_unprocessed_files(self::BATCH_SIZE);
            
            if (empty($css_files)) {
                // No more files to process
                $this->mark_generation_complete();
                return;
            }
            
            foreach ($css_files as $file) {
                $this->process_css_file($file);
            }
            
            // Schedule next batch
            $this->schedule_next_batch();
            
        } catch (\Exception $e) {
            // Log error
            prorank_log('ProRank CSS Cache Generation Error: ' . $e->getMessage());
            
            // Retry in 5 minutes
            as_schedule_single_action(
                time() + 300,
                self::HOOK_NAME,
                $args,
                'prorank-seo'
            );
        }
    }
    
    /**
     * Process individual CSS file
     *
     * @param array $file File data
     * @return void
     */
    private function process_css_file(array $file): void {
        $file_path = $file['path'];
        $cache_key = $file['cache_key'];
        
        try {
            // Read file content
            $content = file_get_contents($file_path);
            
            if ($content === false) {
                throw new \Exception("Failed to read file: {$file_path}");
            }
            
            // Process imports if enabled
            $settings = get_option('prorank_css_optimize_settings', []);
            
            if (!empty($settings['css_inline_imports'])) {
                $content = $this->import_processor->process($content, dirname($file_path));
            }
            
            // Copy font assets and rewrite URLs to cached webfonts directory
            $font_urls = $this->path_rewriter->extractUrls($content);
            $this->copy_font_assets($font_urls, dirname($file_path));
            $content = $this->normalize_font_urls($font_urls, $content);
            
            // Rewrite paths
            $cache_dir = wp_upload_dir()['basedir'] . '/prorank-cache/css';
            $cache_file = $cache_dir . '/' . $cache_key . '.css';
            $content = $this->path_rewriter->rewrite($content, $file_path, $cache_file);
            
            // Minify content
            $minified = $this->minifier->minify($content, $file_path);
            
            // Save to cache
            $this->save_to_cache($cache_key, $minified, $file);
            
            // Mark as processed
            $this->mark_file_processed($file);
            
            // Update statistics
            $this->update_statistics($file, strlen($content), strlen($minified));
            
        } catch (\Exception $e) {
            // Log error for this file
            prorank_log("ProRank CSS Processing Error for {$file_path}: " . $e->getMessage());
            
            // Mark file as failed
            $this->mark_file_failed($file, $e->getMessage());
        }
    }
    
        /**
     * Copy font assets referenced in CSS to the cached webfonts directory.
     */
    private function copy_font_assets(array $urls, string $source_dir): void {
        $upload_dir = wp_upload_dir();
        $target_dir = trailingslashit($upload_dir['basedir']) . 'prorank-cache/webfonts';
        $site_url = \home_url();
        $site_parts = wp_parse_url($site_url);
        $site_host = $site_parts['host'] ?? '';
        $site_scheme = $site_parts['scheme'] ?? 'https';

        $font_exts = ['woff2', 'woff', 'ttf', 'eot', 'otf', 'svg'];

        if (!is_dir($target_dir)) {
            wp_mkdir_p($target_dir);
        }

        foreach ($urls as $url) {
            $parsed = wp_parse_url($url);
            $path = $parsed['path'] ?? '';
            if (!$path) {
                continue;
            }
            $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
            if (!in_array($ext, $font_exts, true)) {
                continue;
            }

            // Skip external hosts
            if (!empty($parsed['host']) && $site_host && strcasecmp($parsed['host'], $site_host) !== 0) {
                continue;
            }

            $filename = basename($path);

            // Try to resolve font on disk first (handles relative and root-relative paths)
            if (strpos($path, '/') === 0) {
                $source_path = realpath(ABSPATH . ltrim($path, '/'));
            } else {
                $source_path = realpath(rtrim($source_dir, '/\\') . '/' . ltrim($path, '/'));
            }

            if ($source_path && file_exists($source_path)) {
                @copy($source_path, $target_dir . '/' . $filename);
                continue;
            }

            // Fallback: download same-site font if not found locally (covers protocol-relative/absolute)
            $download_url = $url;
            if (empty($parsed['scheme'])) {
                if (!empty($parsed['host'])) {
                    $download_url = $site_scheme . ':' . $url;
                } else {
                    $download_url = rtrim($site_url, '/') . '/' . ltrim($path, '/');
                }
            }

            $response = wp_remote_get($download_url, ['timeout' => 10]);
            if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
                $body = wp_remote_retrieve_body($response);
                if (!empty($body)) {
                    file_put_contents($target_dir . '/' . $filename, $body);
                }
            }
        }
    }

    /**
     * Normalize font URLs to the cached webfonts directory.
     */
    private function normalize_font_urls(array $urls, string $content): string {
        foreach ($urls as $url) {
            $path = wp_parse_url($url, PHP_URL_PATH);
            if (!$path) {
                continue;
            }
            $ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
            if (!in_array($ext, ['woff2', 'woff', 'ttf', 'eot', 'otf', 'svg'], true)) {
                continue;
            }
            $filename = basename($path);
            $content = str_replace($url, '/prorank-cache/webfonts/' . $filename, $content);
        }
        return $content;
    }

    /**
     * Get unprocessed CSS files
     *
     * @param int $limit Number of files to get
     * @return array
     */
    private function get_unprocessed_files(int $limit): array {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_css_cache';
        
        // 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 ?: [];
    }
    
    /**
     * Save minified content to cache
     *
     * @param string $cache_key Cache key
     * @param string $content Minified content
     * @param array $file File data
     * @return void
     */
    private function save_to_cache(string $cache_key, string $content, array $file): void {
        $cache_dir = wp_upload_dir()['basedir'] . '/prorank-cache/css';
        
        // Create directory if not exists
        if (!file_exists($cache_dir)) {
            wp_mkdir_p($cache_dir);
        }
        
        // Save minified file
        $cache_file = $cache_dir . '/' . $cache_key . '.css';
        file_put_contents($cache_file, $content);
        
        // Save gzipped version if enabled
        $settings = get_option('prorank_css_optimize_settings', []);
        
        if (!empty($settings['css_gzip'])) {
            $gzipped = gzencode($content, 9);
            file_put_contents($cache_file . '.gz', $gzipped);
        }
    }
    
    /**
     * Mark file as processed
     *
     * @param array $file File data
     * @return void
     */
    private function mark_file_processed(array $file): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_css_cache';
        
        $wpdb->update(
            $table,
            [
                'status' => 'processed',
                'processed_at' => current_time('mysql'),
            ],
            ['id' => $file['id']],
            ['%s', '%s'],
            ['%d']
        );
    }
    
    /**
     * Mark file as failed
     *
     * @param array $file File data
     * @param string $error Error message
     * @return void
     */
    private function mark_file_failed(array $file, string $error): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_css_cache';
        
        $wpdb->update(
            $table,
            [
                'status' => 'failed',
                'error_message' => $error,
                'processed_at' => current_time('mysql'),
            ],
            ['id' => $file['id']],
            ['%s', '%s', '%s'],
            ['%d']
        );
    }
    
    /**
     * Update optimization statistics
     *
     * @param array $file File data
     * @param int $original_size Original size
     * @param int $minified_size Minified size
     * @return void
     */
    private function update_statistics(array $file, int $original_size, int $minified_size): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_css_cache';
        
        $wpdb->update(
            $table,
            [
                'original_size' => $original_size,
                'minified_size' => $minified_size,
                'compression_ratio' => round((1 - $minified_size / $original_size) * 100, 2),
            ],
            ['id' => $file['id']],
            ['%d', '%d', '%f'],
            ['%d']
        );
    }
    
    /**
     * Schedule next batch
     *
     * @return void
     */
    private function schedule_next_batch(): void {
        as_schedule_single_action(
            time() + 10, // Process next batch in 10 seconds
            self::HOOK_NAME,
            [],
            'prorank-seo'
        );
    }
    
    /**
     * Mark generation as complete
     *
     * @return void
     */
    private function mark_generation_complete(): void {
        update_option('prorank_css_cache_generation_complete', true);
        update_option('prorank_css_cache_generation_timestamp', time());
        
        // Trigger completion action
        do_action('prorank_css_cache_generation_complete');
    }
    
    /**
     * Ensure database table exists
     *
     * @return void
     */
    private function ensure_table_exists(): void {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'prorank_css_cache';
        $charset_collate = $wpdb->get_charset_collate();
        
        $sql = "CREATE TABLE IF NOT EXISTS {$table_name} (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            cache_key varchar(32) NOT NULL,
            path varchar(255) NOT NULL,
            url varchar(255) NOT NULL,
            status varchar(20) DEFAULT 'pending',
            priority int(11) DEFAULT 0,
            original_size bigint(20) UNSIGNED DEFAULT 0,
            minified_size bigint(20) UNSIGNED DEFAULT 0,
            compression_ratio float DEFAULT 0,
            error_message text,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            processed_at datetime DEFAULT NULL,
            PRIMARY KEY (id),
            UNIQUE KEY cache_key (cache_key),
            KEY status (status),
            KEY priority (priority)
        ) {$charset_collate};";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
    }
    
    /**
     * Queue CSS files for processing
     *
     * @param array $files Array of file paths
     * @return void
     */
    public function queue_files(array $files): void {
        global $wpdb;
        
        $table = $wpdb->prefix . 'prorank_css_cache';
        $this->ensure_table_exists();
        
        foreach ($files as $priority => $file_path) {
            $cache_key = md5($file_path);
            $url = str_replace(ABSPATH, site_url('/'), $file_path);
            
            // Insert or update
            $wpdb->replace(
                $table,
                [
                    'cache_key' => $cache_key,
                    'path' => $file_path,
                    'url' => $url,
                    'status' => 'pending',
                    'priority' => is_numeric($priority) ? $priority : 0,
                    'created_at' => current_time('mysql'),
                ],
                ['%s', '%s', '%s', '%s', '%d', '%s']
            );
        }
        
        // Trigger processing
        if (!as_next_scheduled_action(self::HOOK_NAME)) {
            $this->schedule_next_batch();
        }
    }
}
