<?php

namespace VulnTitan;

use VulnTitan\Admin\Admin;
use VulnTitan\Cli\ScanCommand;
use VulnTitan\Services\CaptchaService;
use VulnTitan\Services\CommentSpamService;
use VulnTitan\Services\FirewallService;
use VulnTitan\Services\FormSpamService;
use VulnTitan\Services\LoginAccessService;
use VulnTitan\Services\LoginSecurityService;
use VulnTitan\Services\VulnerabilityRiskService;
use VulnTitan\Services\WeeklySummaryEmailService;

class Plugin {
    protected const INTEGRITY_QUEUE_CLEANUP_HOOK = 'vulntitan_cleanup_integrity_queues';
    protected const INTEGRITY_QUEUE_MAX_AGE = DAY_IN_SECONDS;
    protected const MALWARE_BACKUP_CLEANUP_HOOK = 'vulntitan_cleanup_malware_backups';
    protected const MALWARE_BACKUP_MAX_AGE = 14 * DAY_IN_SECONDS;
    protected const MALWARE_BACKUP_KEEP_PER_SOURCE = 5;
    protected const MALWARE_BACKUP_MAX_FILES = 500;
    protected const FIREWALL_LOG_CLEANUP_HOOK = 'vulntitan_cleanup_firewall_logs';
    protected const WEEKLY_SUMMARY_EMAIL_HOOK = 'vulntitan_send_weekly_summary_email';
    protected const COMPONENT_HEALTH_CHECK_TRANSIENT = 'vulntitan_component_health_check';
    protected const COMPONENT_HEALTH_CHECK_TTL = 12 * HOUR_IN_SECONDS;
    protected const COMPONENT_HEALTH_CHECK_RETRY_TTL = 15 * MINUTE_IN_SECONDS;

    public $admin;

    public function init(): void
    {
        $this->maybe_generate_baseline();
        $this->maybeEnsureFirewallComponents();
        $this->register_scheduled_events();
        $this->register_cli_commands();
        LoginAccessService::boot();
        LoginSecurityService::boot();
        CaptchaService::boot();
        CommentSpamService::boot();
        FormSpamService::boot();
        $this->admin = new Admin();
        $this->admin->init();
    }

    public static function activate(): void
    {
        add_filter('cron_schedules', [self::class, 'registerCronSchedules']);
        self::schedule_integrity_queue_cleanup();
        self::schedule_malware_backup_cleanup();
        self::schedule_firewall_log_cleanup();
        self::schedule_weekly_summary_email();
        FirewallService::createTable();
        VulnerabilityRiskService::createTables();
        FirewallService::installMuLoader();
    }

    public static function deactivate(): void
    {
        wp_clear_scheduled_hook(self::INTEGRITY_QUEUE_CLEANUP_HOOK);
        wp_clear_scheduled_hook(self::MALWARE_BACKUP_CLEANUP_HOOK);
        wp_clear_scheduled_hook(self::FIREWALL_LOG_CLEANUP_HOOK);
        wp_clear_scheduled_hook(self::WEEKLY_SUMMARY_EMAIL_HOOK);
        delete_transient(self::COMPONENT_HEALTH_CHECK_TRANSIENT);
        FirewallService::removeMuLoader();
    }

    protected function register_scheduled_events(): void
    {
        add_filter('cron_schedules', [self::class, 'registerCronSchedules']);
        add_action(self::INTEGRITY_QUEUE_CLEANUP_HOOK, [$this, 'cleanup_integrity_queue_files']);
        add_action(self::MALWARE_BACKUP_CLEANUP_HOOK, [$this, 'cleanup_malware_backup_files']);
        add_action(self::FIREWALL_LOG_CLEANUP_HOOK, [$this, 'cleanup_firewall_logs']);
        add_action(self::WEEKLY_SUMMARY_EMAIL_HOOK, [WeeklySummaryEmailService::class, 'sendScheduledDigest']);
        self::schedule_integrity_queue_cleanup();
        self::schedule_malware_backup_cleanup();
        self::schedule_firewall_log_cleanup();
        self::schedule_weekly_summary_email();
    }

    protected static function schedule_integrity_queue_cleanup(): void
    {
        if (wp_next_scheduled(self::INTEGRITY_QUEUE_CLEANUP_HOOK)) {
            return;
        }

        wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', self::INTEGRITY_QUEUE_CLEANUP_HOOK);
    }

    protected static function schedule_malware_backup_cleanup(): void
    {
        if (wp_next_scheduled(self::MALWARE_BACKUP_CLEANUP_HOOK)) {
            return;
        }

        wp_schedule_event(time() + (2 * HOUR_IN_SECONDS), 'daily', self::MALWARE_BACKUP_CLEANUP_HOOK);
    }

    protected static function schedule_firewall_log_cleanup(): void
    {
        if (wp_next_scheduled(self::FIREWALL_LOG_CLEANUP_HOOK)) {
            return;
        }

        wp_schedule_event(time() + (3 * HOUR_IN_SECONDS), 'daily', self::FIREWALL_LOG_CLEANUP_HOOK);
    }

    protected static function schedule_weekly_summary_email(): void
    {
        if (wp_next_scheduled(self::WEEKLY_SUMMARY_EMAIL_HOOK)) {
            return;
        }

        wp_schedule_event(
            WeeklySummaryEmailService::getNextScheduleTimestamp(),
            'vulntitan_weekly',
            self::WEEKLY_SUMMARY_EMAIL_HOOK
        );
    }

    public static function registerCronSchedules(array $schedules): array
    {
        if (!isset($schedules['vulntitan_weekly'])) {
            $schedules['vulntitan_weekly'] = [
                'interval' => 7 * DAY_IN_SECONDS,
                'display' => __('Once Weekly (VulnTitan)', 'vulntitan'),
            ];
        }

        return $schedules;
    }

    public function maybe_generate_baseline()
    {
        if (!defined('VULNTITAN_BASELINE_FILE')) return;

        if (!file_exists(VULNTITAN_BASELINE_FILE)) {
            update_option('vulntitan_baseline_status', 'missing');
        }
    }

    protected function maybeEnsureFirewallComponents(): void
    {
        if (!$this->shouldRunComponentHealthCheck()) {
            return;
        }

        $success = $this->ensure_firewall_components();
        set_transient(
            self::COMPONENT_HEALTH_CHECK_TRANSIENT,
            $success ? 'ok' : 'retry',
            $success ? self::COMPONENT_HEALTH_CHECK_TTL : self::COMPONENT_HEALTH_CHECK_RETRY_TTL
        );
    }

    protected function shouldRunComponentHealthCheck(): bool
    {
        if (defined('WP_INSTALLING') && WP_INSTALLING) {
            return false;
        }

        if (get_transient(self::COMPONENT_HEALTH_CHECK_TRANSIENT)) {
            return false;
        }

        if (defined('WP_CLI') && WP_CLI) {
            return true;
        }

        if (defined('DOING_CRON') && DOING_CRON) {
            return true;
        }

        if (function_exists('wp_doing_ajax') && wp_doing_ajax()) {
            return false;
        }

        return $this->isVulnTitanAdminRequest();
    }

    protected function isVulnTitanAdminRequest(): bool
    {
        if (!is_admin()) {
            return false;
        }

        $page = isset($_GET['page']) ? sanitize_key((string) wp_unslash($_GET['page'])) : '';

        return in_array($page, [
            'vulntitan',
            'vulntitan-firewall',
            'vulntitan-live-feed',
        ], true);
    }

    protected function ensure_firewall_components(): bool
    {
        $firewallReady = FirewallService::ensureTable();
        $muLoader = FirewallService::installMuLoader();
        $vulnerabilityReady = VulnerabilityRiskService::ensureTables();

        return $firewallReady && !empty($muLoader['success']) && $vulnerabilityReady;
    }

    protected function register_cli_commands(): void
    {
        if (!defined('WP_CLI') || !WP_CLI || !class_exists('\WP_CLI')) {
            return;
        }

        \WP_CLI::add_command('vulntitan scan', ScanCommand::class);
    }

    public function cleanup_integrity_queue_files(): void
    {
        if (!defined('VULNTITAN_PLUGIN_DIR')) {
            return;
        }

        $queueDir = VULNTITAN_PLUGIN_DIR . '/storage/queues';
        if (!is_dir($queueDir)) {
            return;
        }

        $threshold = time() - self::INTEGRITY_QUEUE_MAX_AGE;
        $queueFiles = glob($queueDir . '/*.queue');
        if (!is_array($queueFiles) || !$queueFiles) {
            return;
        }

        foreach ($queueFiles as $queueFile) {
            if (!is_file($queueFile)) {
                continue;
            }

            $modifiedAt = filemtime($queueFile);
            if ($modifiedAt !== false && $modifiedAt > $threshold) {
                continue;
            }

            @unlink($queueFile);
        }
    }

    public function cleanup_malware_backup_files(): void
    {
        $backupDirs = $this->get_malware_backup_directories();
        if (!$backupDirs) {
            return;
        }

        $threshold = time() - self::MALWARE_BACKUP_MAX_AGE;
        $allBackups = [];
        $groupedBackups = [];

        foreach ($backupDirs as $backupDir) {
            $backupFiles = glob(rtrim($backupDir, '/\\') . '/*.bak');
            if (!is_array($backupFiles) || !$backupFiles) {
                continue;
            }

            foreach ($backupFiles as $backupFile) {
                if (!is_file($backupFile)) {
                    continue;
                }

                $modifiedAt = filemtime($backupFile);
                if ($modifiedAt === false) {
                    $modifiedAt = 0;
                }

                if ($modifiedAt < $threshold) {
                    @unlink($backupFile);
                    continue;
                }

                $allBackups[$backupFile] = $modifiedAt;
                $groupKey = $this->extract_malware_backup_group_key($backupFile);
                if (!isset($groupedBackups[$groupKey])) {
                    $groupedBackups[$groupKey] = [];
                }

                $groupedBackups[$groupKey][$backupFile] = $modifiedAt;
            }
        }

        foreach ($groupedBackups as $groupFiles) {
            arsort($groupFiles, SORT_NUMERIC);
            $index = 0;

            foreach ($groupFiles as $backupFile => $modifiedAt) {
                $index++;

                if ($index <= self::MALWARE_BACKUP_KEEP_PER_SOURCE) {
                    continue;
                }

                @unlink($backupFile);
                unset($allBackups[$backupFile]);
            }
        }

        if (count($allBackups) <= self::MALWARE_BACKUP_MAX_FILES) {
            return;
        }

        arsort($allBackups, SORT_NUMERIC);
        $index = 0;

        foreach ($allBackups as $backupFile => $modifiedAt) {
            $index++;

            if ($index <= self::MALWARE_BACKUP_MAX_FILES) {
                continue;
            }

            @unlink($backupFile);
        }
    }

    public function cleanup_firewall_logs(): void
    {
        FirewallService::cleanupOldLogs();
    }

    protected function get_malware_backup_directories(): array
    {
        $directories = [];

        if (defined('VULNTITAN_PLUGIN_DIR')) {
            $directories[] = VULNTITAN_PLUGIN_DIR . '/storage/backups/malware-fixes';
        }

        if (defined('ABSPATH')) {
            $wpRoot = realpath(ABSPATH);
            if ($wpRoot) {
                $directories[] = dirname($wpRoot) . '/vulntitan-secure-backups/malware-fixes';
            }
        }

        $existingDirs = [];
        foreach (array_unique($directories) as $dir) {
            if (is_dir($dir)) {
                $existingDirs[] = $dir;
            }
        }

        return $existingDirs;
    }

    protected function extract_malware_backup_group_key(string $backupFile): string
    {
        $name = basename($backupFile);

        if (preg_match('/^(.+)\.\d{8}-\d{6}\.[A-Za-z0-9]{6}\.bak$/', $name, $matches)) {
            return (string) $matches[1];
        }

        return $name;
    }
}
