<?php

namespace WPSN\Modules\Diagnostics;

use WPSN\Core\Container;

defined('ABSPATH') || exit;

/**
 * Scheduled Quality Gate runs.
 *
 * Uses an hourly cron tick (wpsn_quality_gate_tick) to decide whether it's time
 * to run daily/weekly checks.
 */
final class QualityGateAutomation
{
    public const CRON_TICK = 'wpsn_quality_gate_tick';

    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
    {
        $settings = $this->container->get('settings');

        $mode = (string) $settings->get('quality_gate.automation.schedule', 'off');
        if (!in_array($mode, ['daily', 'weekly'], true)) {
            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
    {
        $settings = $this->container->get('settings');

        $mode = (string) $settings->get('quality_gate.automation.schedule', 'off');
        if (!in_array($mode, ['daily', 'weekly'], true)) {
            return;
        }

        $hour = (int) $settings->get('quality_gate.automation.hour_utc', 4);
        $hour = max(0, min(23, $hour));

        // Tick is hourly, so we only consider the configured hour.
        $nowHour = (int) gmdate('G');
        if ($nowHour !== $hour) {
            return;
        }

        $due = false;
        $lockKey = '';
        $lockVal = '';

        if ($mode === 'daily') {
            $today = (string) gmdate('Y-m-d');
            $lockKey = 'wpsn_quality_gate_last_daily';
            $lockVal = $today;
            $last = (string) get_option($lockKey, '');
            $due = ($last !== $today);
        } elseif ($mode === 'weekly') {
            $weekday = (int) $settings->get('quality_gate.automation.weekday_utc', 1);
            $weekday = max(0, min(6, $weekday));
            $nowW = (int) gmdate('w');
            if ($nowW !== $weekday) {
                return;
            }

            $weekKey = (string) (gmdate('o') . '-W' . gmdate('W'));
            $lockKey = 'wpsn_quality_gate_last_weekly';
            $lockVal = $weekKey;
            $last = (string) get_option($lockKey, '');
            $due = ($last !== $weekKey);
        }

        if (!$due || $lockKey === '' || $lockVal === '') {
            return;
        }

        // Try to set lock first to avoid duplicate triggers if cron fires twice.
        update_option($lockKey, $lockVal, false);

        $svc = new QualityGateService($this->container);
        $res = $svc->runAllChecks([
            'trigger' => 'cron',
            'schedule_mode' => $mode,
        ]);
        $svc->saveGlobalResults($res);

        /**
         * Fires after a Quality Gate run is completed.
         *
         * @param array<string,mixed> $res
         * @param array<string,mixed> $context
         */
        do_action('wpsn/quality_gate/run_completed', $res, [
            'trigger' => 'cron',
            'schedule_mode' => $mode,
        ]);
    }
}
