<?php

namespace WPSN\Modules\Diagnostics;

use WPSN\Core\Container;

defined('ABSPATH') || exit;

/**
 * Scheduled platform probe runs used for platform flip notifications.
 *
 * Conservative by default: the schedule is now controlled directly from
 * Tools → Platform Ops. Upgraded installs can still temporarily fall back to
 * the legacy notification-driven daily mode until the UI settings are saved.
 */
final class PlatformOpsAutomation
{
    public const CRON_TICK = 'wpsn_platform_ops_tick';
    private const LAST_DAILY_OPTION = 'wpsn_platform_ops_last_daily';
    private const LAST_WEEKLY_OPTION = 'wpsn_platform_ops_last_weekly';

    private Container $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function register(): void
    {
        add_action('init', [$this, 'ensureCron'], 20);
        add_action(self::CRON_TICK, [$this, 'onScheduleTick']);
    }

    public function ensureCron(): void
    {
        if (!$this->automationEnabled()) {
            if (function_exists('wp_clear_scheduled_hook')) {
                wp_clear_scheduled_hook(self::CRON_TICK);
            }
            return;
        }

        if (!wp_next_scheduled(self::CRON_TICK)) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'hourly', self::CRON_TICK);
        }
    }

    public function onScheduleTick(): void
    {
        $mode = $this->automationMode();
        if (!in_array($mode, ['daily', 'weekly'], true)) {
            return;
        }

        $hour = $this->runHourUtc();
        $nowHour = (int) gmdate('G');
        if ($nowHour !== $hour) {
            return;
        }

        $lockKey = '';
        $lockValue = '';
        $weekday = null;

        if ($mode === 'daily') {
            $today = (string) gmdate('Y-m-d');
            $lockKey = self::LAST_DAILY_OPTION;
            $lockValue = $today;
        } else {
            $weekday = $this->runWeekdayUtc();
            if ((int) gmdate('w') !== $weekday) {
                return;
            }

            $lockKey = self::LAST_WEEKLY_OPTION;
            $lockValue = (string) (gmdate('o') . '-W' . gmdate('W'));
        }

        if ($lockKey === '' || $lockValue === '') {
            return;
        }

        if ((string) get_option($lockKey, '') === $lockValue) {
            return;
        }

        update_option($lockKey, $lockValue, false);

        $repo = new PlatformOpsReportRepository();
        $previous = $repo->getLast();
        $report = (new PlatformOpsProbeService($this->container))->run([
            'trigger' => 'cron',
            'schedule_mode' => $mode,
            'hour_utc' => $hour,
            'weekday_utc' => $weekday,
        ]);
        $repo->save($report);

        /**
         * Fires after a scheduled Platform Ops probe is completed.
         *
         * @param array<string,mixed> $report
         * @param array<string,mixed> $context
         */
        do_action('wpsn/platform_ops/run_completed', $report, [
            'trigger' => 'cron',
            'schedule_mode' => $mode,
            'hour_utc' => $hour,
            'weekday_utc' => $weekday,
            'previous' => $previous,
        ]);
    }

    public function automationMode(): string
    {
        $settings = $this->container->get('settings');
        $configured = $this->automationConfigured();
        $mode = sanitize_key((string) $settings->get('platform_ops.automation.schedule', 'off'));

        if (!$configured) {
            return $this->legacyAutomationEnabled() ? 'daily' : 'off';
        }

        return in_array($mode, ['daily', 'weekly'], true) ? $mode : 'off';
    }

    public function automationConfigured(): bool
    {
        $settings = $this->container->get('settings');
        return (bool) $settings->get('platform_ops.automation.configured', false);
    }

    public function runHourUtc(): int
    {
        $settings = $this->container->get('settings');
        $hour = (int) $settings->get('platform_ops.automation.hour_utc', 6);
        return max(0, min(23, $hour));
    }

    public function runWeekdayUtc(): int
    {
        $settings = $this->container->get('settings');
        $weekday = (int) $settings->get('platform_ops.automation.weekday_utc', 1);
        return max(0, min(6, $weekday));
    }

    public function automationEnabled(): bool
    {
        return in_array($this->automationMode(), ['daily', 'weekly'], true);
    }

    public function flipNotificationSourceEnabled(): bool
    {
        $settings = $this->container->get('settings');
        return (bool) $settings->get('notifications.sources.platform_ops', false);
    }

    public function notificationsOperational(): bool
    {
        $settings = $this->container->get('settings');
        if (!$settings->moduleEnabled('notifications', true)) {
            return false;
        }

        return (bool) $settings->get('notifications.enabled', true);
    }

    public function legacyFallbackActive(): bool
    {
        return !$this->automationConfigured() && $this->legacyAutomationEnabled();
    }

    public function nextDueTimestampUtc(?int $now = null): int
    {
        $mode = $this->automationMode();
        if (!in_array($mode, ['daily', 'weekly'], true)) {
            return 0;
        }

        $now = is_int($now) && $now > 0 ? $now : time();
        $hour = $this->runHourUtc();
        $year = (int) gmdate('Y', $now);
        $month = (int) gmdate('n', $now);
        $day = (int) gmdate('j', $now);

        if ($mode === 'daily') {
            $candidate = gmmktime($hour, 0, 0, $month, $day, $year);
            if ($candidate <= $now) {
                $candidate += DAY_IN_SECONDS;
            }

            return (int) $candidate;
        }

        $targetWeekday = $this->runWeekdayUtc();
        $currentWeekday = (int) gmdate('w', $now);
        $deltaDays = ($targetWeekday - $currentWeekday + 7) % 7;
        $candidate = gmmktime($hour, 0, 0, $month, $day + $deltaDays, $year);
        if ($candidate <= $now) {
            $candidate += WEEK_IN_SECONDS;
        }

        return (int) $candidate;
    }

    /**
     * @param array<string,mixed> $state
     * @return array{tone:string,label:string}
     */
    public static function compactBadgeFromState(array $state): array
    {
        $mode = isset($state['mode']) ? sanitize_key((string) $state['mode']) : 'off';
        $legacyFallback = !empty($state['legacy_fallback']);
        $wpCronDisabled = !empty($state['wp_cron_disabled']);

        if (!in_array($mode, ['daily', 'weekly'], true)) {
            return [
                'tone' => 'info',
                'label' => __('Auto probe off', 'wp-seo-next'),
            ];
        }

        if ($legacyFallback) {
            return [
                'tone' => 'warn',
                'label' => __('Auto probe legacy daily', 'wp-seo-next'),
            ];
        }

        $label = ($mode === 'weekly')
            ? __('Auto probe weekly', 'wp-seo-next')
            : __('Auto probe daily', 'wp-seo-next');

        return [
            'tone' => $wpCronDisabled ? 'warn' : 'ok',
            'label' => $label,
        ];
    }

    public function resetScheduleLocks(): void
    {
        delete_option(self::LAST_DAILY_OPTION);
        delete_option(self::LAST_WEEKLY_OPTION);
    }

    private function legacyAutomationEnabled(): bool
    {
        return $this->notificationsOperational() && $this->flipNotificationSourceEnabled();
    }
}
