<?php

namespace FleekDash\Core;

use FleekDash\Database\Core\MigrationManager;
use FleekDash\Database\Seeders\Accounts as SeedersAccounts;
use FleekDash\Services\SiteGrowthMeterService;

defined('ABSPATH') || exit;

if (!function_exists('fleekdash_log_info')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

class ActivationRunner
{
    private const OPTION_KEY = 'fleekdash_activation_pipeline';
    private const CRON_HOOK = 'fleekdash_async_activation_runner';
    private const STEPS = [
        'old_plugin',
        'migrations',
        'seeders',
        'site_growth_meter',
        'schedule_events',
        'rewrite_flush',
        'complete',
    ];

    /**
     * Schedule the first runner task.
     * Falls back to immediate execution if cron is disabled or scheduling fails.
     */
    public static function scheduleAsyncActivation(): void
    {
        $state = \get_option(self::OPTION_KEY, []);

        if (!empty($state['completed'])) {
            fleekdash_log_info('[FleekDash] Activation already completed, skipping.');
            return;
        }

        $state['next_step'] = $state['next_step'] ?? 0;
        $state['status'] = 'pending';
        $state['started_at'] = $state['started_at'] ?? \current_time('timestamp');
        \update_option(self::OPTION_KEY, $state, false);

        // Try to schedule cron
        $scheduled = self::scheduleRunner();

        /**
         * IMPORTANT:
         * - We only fall back to immediate execution when scheduling REALLY fails.
         * - DISABLE_WP_CRON does NOT mean scheduling is broken; it only disables
         *   the internal HTTP-based trigger. Many sites use a real system cron
         *   to hit wp-cron.php, which will still process scheduled events.
         *
         * Previous behaviour:
         * - If DISABLE_WP_CRON was true, we tried to run the entire activation
         *   pipeline synchronously in a single request (potentially heavy).
         *
         * New behaviour:
         * - Trust the environment: when scheduling succeeds, let the cron
         *   (or external scheduler) process steps asynchronously.
         * - Only execute immediately when scheduling fails with an error.
         */
        if (!$scheduled) {
            fleekdash_log_info('[FleekDash] Cron scheduling failed, executing activation pipeline immediately.');
            self::handleScheduledRunner();
        } else {
            fleekdash_log_info('[FleekDash] Activation pipeline scheduled for async execution.');
        }
    }

    /**
     * Cron callback that executes one activation step per run.
     */
    public static function handleScheduledRunner(): void
    {
        $state = \get_option(self::OPTION_KEY, []);
        if (!empty($state['completed'])) {
            return;
        }

        $stepIndex = $state['next_step'] ?? 0;
        if ($stepIndex >= count(self::STEPS)) {
            self::markComplete();
            return;
        }

        $currentStep = self::STEPS[$stepIndex];
        $result = self::executeStep($currentStep);

        $state['last_step'] = $currentStep;
        $state['last_message'] = $result['message'] ?? '';
        $state['last_result'] = [
            'timestamp' => \current_time('mysql'),
            'retry' => !empty($result['retry']),
        ];

        if (!empty($result['error'])) {
            $state['error'] = $result['error'];
        }

        if (!empty($result['retry'])) {
            \update_option(self::OPTION_KEY, $state, false);
            $scheduled = self::scheduleRunner();

            // If cron scheduling failed, continue immediately (single fallback)
            if (!$scheduled) {
                fleekdash_log_info('[FleekDash] Cron scheduling failed, continuing activation pipeline immediately.');
                self::handleScheduledRunner();
            }
            return;
        }

        $state['next_step'] = $stepIndex + 1;
        \update_option(self::OPTION_KEY, $state, false);

        if ($state['next_step'] >= count(self::STEPS)) {
            self::markComplete();
        } else {
            $scheduled = self::scheduleRunner();

            // If cron scheduling failed, continue immediately (single fallback)
            if (!$scheduled) {
                fleekdash_log_info('[FleekDash] Cron scheduling failed, continuing activation pipeline immediately.');
                self::handleScheduledRunner();
            }
        }
    }

    private static function markComplete(): void
    {
        \update_option(self::OPTION_KEY, [
            'completed' => true,
            'finished_at' => \current_time('mysql'),
        ], false);
        fleekdash_log_info('[FleekDash] Activation runner completed.');
    }

    /**
     * Schedule the runner cron event.
     *
     * @return bool True if scheduled successfully, false otherwise
     */
    private static function scheduleRunner(): bool
    {
        // Check if already scheduled
        if (wp_next_scheduled(self::CRON_HOOK)) {
            return true;
        }

        // Try to schedule
        $result = \wp_schedule_single_event(time() + 5, self::CRON_HOOK);

        if (is_wp_error($result)) {
            fleekdash_log_error('[FleekDash] Failed to schedule activation runner: ' . $result->get_error_message());
            return false;
        }

        return true;
    }

    private static function executeStep(string $step): array
    {
        return match ($step) {
            'old_plugin' => self::runOldPluginStep(),
            'migrations' => self::runMigrationStep(),
            'seeders' => self::runSeedersStep(),
            'site_growth_meter' => self::runSiteGrowthMeterStep(),
            'schedule_events' => self::runScheduleEventsStep(),
            'rewrite_flush' => self::runRewriteFlushStep(),
            'complete' => ['message' => 'Activation pipeline complete.'],
            default => ['message' => 'Unknown activation step.', 'retry' => false],
        };
    }

    private static function runOldPluginStep(): array
    {
        OldPluginRemover::removeOldPlugin();
        OldPluginRemover::processScheduledRemoval();
        fleekdash_log_info('[FleekDash] Legacy plugin cleanup flagged during activation.');

        return ['message' => 'Legacy plugin removal queued.'];
    }

    private static function runMigrationStep(): array
    {
        $manager = new MigrationManager();
        $result = $manager->runBatch(1);

        // Handle case where there's nothing to migrate
        if (isset($result['message']) && $result['message'] === 'Nothing to migrate') {
            fleekdash_log_info('[FleekDash] No pending migrations.');
            return ['message' => 'No pending migrations.'];
        }

        $migrationsCount = count($result['migrations'] ?? []);
        $pendingRemaining = $result['pending_remaining'] ?? 0;
        $hasMore = !empty($result['has_more']) && empty($result['stopped']);

        $message = sprintf(
            'Ran %d migration(s) in batch #%d. Pending: %d.',
            $migrationsCount,
            $result['batch'] ?? 0,
            $pendingRemaining
        );

        fleekdash_log_info('[FleekDash] ' . $message);

        // If migration failed, return error
        if (!empty($result['stopped'])) {
            $lastMigration = $migrationsCount > 0 ? $result['migrations'][$migrationsCount - 1] : null;
            $error = $lastMigration['error'] ?? 'Migration stopped';

            fleekdash_log_error('[FleekDash] Migration failed: ' . $error);

            return [
                'message' => $message,
                'error' => $error,
            ];
        }

        // If there are more migrations, retry
        return [
            'message' => $message,
            'retry' => $hasMore,
        ];
    }

    private static function runSeedersStep(): array
    {
        try {
            SeedersAccounts::run();
            fleekdash_log_info('[FleekDash] Seeders executed as part of activation.');

            return ['message' => 'Seeders completed.'];
        } catch (\Throwable $e) {
            fleekdash_log_error('[FleekDash] Seeders failed: ' . $e->getMessage());
            return ['message' => 'Seeders failed.', 'error' => $e->getMessage()];
        }
    }

    private static function runSiteGrowthMeterStep(): array
    {
        try {
            $meter = new SiteGrowthMeterService();
            $result = $meter->initializeSiteGrowthMeter();

            fleekdash_log_info('[FleekDash] Site Growth Meter initialization: ' . ($result['message'] ?? 'completed'));

            return ['message' => $result['message'] ?? 'Site Growth Meter initialized.'];
        } catch (\Throwable $e) {
            fleekdash_log_error('[FleekDash] Site Growth Meter error: ' . $e->getMessage());
            return ['message' => 'Site Growth Meter failed.', 'error' => $e->getMessage()];
        }
    }

    private static function runScheduleEventsStep(): array
    {
        $events = [
            'fleekdash_search_background_index' => ['interval' => 'hourly', 'delay' => 60],
            'fleekdash_daily_cache_cleanup' => ['interval' => 'daily', 'delay' => 300],
        ];

        foreach ($events as $hook => $config) {
            if (!wp_next_scheduled($hook)) {
                \wp_schedule_event(time() + $config['delay'], $config['interval'], $hook);
            }
        }

        // Schedule Growth Score monthly snapshot
        if (class_exists('FleekDash\\Services\\SiteGrowthMeter\\SnapshotScheduler')) {
            \FleekDash\Services\SiteGrowthMeter\SnapshotScheduler::schedule();
        }

        fleekdash_log_info('[FleekDash] Scheduled activation-based cron jobs.');

        return ['message' => 'Cron jobs registered.'];
    }

    private static function runRewriteFlushStep(): array
    {
        \flush_rewrite_rules();
        fleekdash_log_info('[FleekDash] Rewrite rules flushed after activation.');

        return ['message' => 'Rewrite rules flushed.'];
    }

    /**
     * Get current activation pipeline status.
     * Useful for debugging and admin notices.
     *
     * @return array Status information
     */
    public static function getStatus(): array
    {
        $state = \get_option(self::OPTION_KEY, []);
        $nextScheduled = wp_next_scheduled(self::CRON_HOOK);

        return [
            'completed' => !empty($state['completed']),
            'current_step' => self::STEPS[$state['next_step'] ?? 0] ?? 'unknown',
            'next_step_index' => $state['next_step'] ?? 0,
            'last_step' => $state['last_step'] ?? null,
            'last_message' => $state['last_message'] ?? null,
            'error' => $state['error'] ?? null,
            'started_at' => $state['started_at'] ?? null,
            'finished_at' => $state['finished_at'] ?? null,
            'cron_scheduled' => $nextScheduled ? date('Y-m-d H:i:s', $nextScheduled) : false,
            'cron_disabled' => defined('DISABLE_WP_CRON') && DISABLE_WP_CRON,
            'all_steps' => self::STEPS,
        ];
    }

    /**
     * Manually trigger the activation runner (for debugging/admin).
     */
    public static function triggerManually(): array
    {
        fleekdash_log_info('[FleekDash] Activation runner triggered manually.');
        self::handleScheduledRunner();
        return self::getStatus();
    }
}
