<?php
/**
 * LuxStrat Scheduler
 *
 * Fires every Sunday at 05:00 Luxembourg local time.
 * Produces exactly 1 post per run → 4 posts per month.
 *
 * TWO-LAYER reliability:
 *   1. Real weekly WP-Cron event at Sunday 05:00 CET (best case)
 *   2. Self-healing fallback on every page load (init hook):
 *      checks if Sunday's run was missed and fires it immediately.
 *
 * Daily transient lock prevents double-execution.
 * No wp-config changes. No cPanel cron. Fully automatic.
 *
 * @package DamalionBlogAI
 * @subpackage LuxStrat
 */

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

class DBA_LuxStrat_Scheduler {

    private static $instance = null;

    const CRON_HOOK = 'dba_luxstrat_cron';
    const TIMEZONE  = 'Europe/Luxembourg';
    const TARGET_DAY  = 'Sunday';
    const TARGET_HOUR = 5;  // 05:00

    public static function get_instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct() {
        add_filter( 'cron_schedules', array( $this, 'register_weekly_schedule' ) );
        add_action( self::CRON_HOOK,  array( $this, 'run' ) );

        // Self-healing: check on every page load if we missed Sunday's run
        add_action( 'init', array( $this, 'maybe_catch_up' ), 99 );

        // Re-register cron if missing (admin only, lightweight)
        add_action( 'admin_init', array( __CLASS__, 'ensure_scheduled' ) );
    }

    /* ──────────────────────────────────────────────
     *  WEEKLY SCHEDULE
     * ────────────────────────────────────────────── */

    public function register_weekly_schedule( $schedules ) {
        if ( ! isset( $schedules['weekly'] ) ) {
            $schedules['weekly'] = array(
                'interval' => 604800,
                'display'  => 'Once Weekly',
            );
        }
        return $schedules;
    }

    private static function next_occurrence( $day_name, $hour, $tz_string ) {
        try {
            $tz  = new DateTimeZone( $tz_string );
            $now = new DateTime( 'now', $tz );

            // Try today first
            if ( strtolower( $now->format( 'l' ) ) === strtolower( $day_name ) ) {
                $today = clone $now;
                $today->setTime( (int) $hour, 0, 0 );
                if ( $today > $now ) {
                    return $today->getTimestamp();
                }
            }

            $target = clone $now;
            $target->modify( 'next ' . $day_name );
            $target->setTime( (int) $hour, 0, 0 );
            return $target->getTimestamp();
        } catch ( \Exception $e ) {
            return time() + 3600;
        }
    }

    /* ──────────────────────────────────────────────
     *  ACTIVATE / DEACTIVATE
     * ────────────────────────────────────────────── */

    public static function activate() {
        if ( ! isset( wp_get_schedules()['weekly'] ) ) {
            add_filter( 'cron_schedules', function( $s ) {
                $s['weekly'] = array( 'interval' => 604800, 'display' => 'Once Weekly' );
                return $s;
            } );
        }
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            $next = self::next_occurrence( self::TARGET_DAY, self::TARGET_HOUR, self::TIMEZONE );
            wp_schedule_event( $next, 'weekly', self::CRON_HOOK );
            error_log( 'LuxStrat: Cron scheduled — next: ' . gmdate( 'Y-m-d H:i:s', $next ) . ' UTC' );
        }
    }

    public static function deactivate() {
        wp_clear_scheduled_hook( self::CRON_HOOK );
        error_log( 'LuxStrat: Cron hook removed.' );
    }

    public static function ensure_scheduled() {
        $settings = get_option( 'dba_luxstrat_settings', array() );
        if ( empty( $settings['enabled'] ) ) return;
        if ( ! wp_next_scheduled( self::CRON_HOOK ) ) {
            self::activate();
            error_log( 'LuxStrat: Cron was missing — rescheduled.' );
        }
    }

    /* ──────────────────────────────────────────────
     *  SELF-HEALING CATCH-UP (runs on every page load)
     *
     *  If it's Sunday and past 05:00 CET but we haven't
     *  posted yet today → fire immediately.
     *  Lightweight: 1 transient check, exits fast on non-Sunday.
     * ────────────────────────────────────────────── */

    public function maybe_catch_up() {
        // Quick bail: is module enabled?
        $settings = get_option( 'dba_luxstrat_settings', array() );
        if ( empty( $settings['enabled'] ) ) return;

        try {
            $tz  = new DateTimeZone( self::TIMEZONE );
            $now = new DateTime( 'now', $tz );
        } catch ( \Exception $e ) {
            return;
        }

        // Quick bail: not Sunday → exit
        if ( (int) $now->format( 'N' ) !== 7 ) return;

        // Quick bail: before target hour → exit
        if ( (int) $now->format( 'G' ) < self::TARGET_HOUR ) return;

        // Quick bail: already ran today → exit
        $lock_key = 'dba_luxstrat_ran_' . $now->format( 'Y-m-d' );
        if ( get_transient( $lock_key ) ) return;

        // ── We're on Sunday, past 05:00, and haven't run yet → fire now ──
        error_log( 'LuxStrat catch-up: Sunday ' . $now->format( 'H:i' ) . ' CET — WP-Cron missed, running now.' );

        // Set lock FIRST to prevent concurrent requests from also triggering
        set_transient( $lock_key, true, DAY_IN_SECONDS );

        // Run in a shutdown hook so we don't delay the page load
        add_action( 'shutdown', array( $this, 'run_catchup' ) );
    }

    /**
     * Catch-up execution (called from shutdown hook).
     */
    public function run_catchup() {
        // Ignore user abort so the generation completes
        ignore_user_abort( true );

        // Extend execution time
        if ( function_exists( 'set_time_limit' ) ) {
            @set_time_limit( 300 );
        }

        // v2.15.0: Global conflict lock
        if ( class_exists( 'DBA_Conflict_Center' ) && ! DBA_Conflict_Center::acquire( 'luxstrat', self::CRON_HOOK ) ) {
            // Clear daily lock so catch-up retries on next page load
            try {
                $tz  = new DateTimeZone( self::TIMEZONE );
                $now = new DateTime( 'now', $tz );
                delete_transient( 'dba_luxstrat_ran_' . $now->format( 'Y-m-d' ) );
            } catch ( \Exception $e ) {}
            error_log( 'LuxStrat catch-up: Another module running — will retry next page load' );
            return;
        }

        $generator = DBA_LuxStrat_Generator::get_instance();
        $result    = $generator->run();

        if ( is_wp_error( $result ) ) {
            error_log( 'LuxStrat catch-up: Error — ' . $result->get_error_message() );
            // Clear lock so it retries on next page load
            try {
                $tz  = new DateTimeZone( self::TIMEZONE );
                $now = new DateTime( 'now', $tz );
                delete_transient( 'dba_luxstrat_ran_' . $now->format( 'Y-m-d' ) );
            } catch ( \Exception $e ) {}
        } else {
            error_log( 'LuxStrat catch-up: Success — Post #' . $result['post_id'] . ' "' . $result['title'] . '"' );
        }

        // v2.15.0: Release global lock
        if ( class_exists( 'DBA_Conflict_Center' ) ) DBA_Conflict_Center::release( 'luxstrat' );
    }

    /* ──────────────────────────────────────────────
     *  MAIN CRON CALLBACK (best-case path)
     * ────────────────────────────────────────────── */

    public function run() {
        $settings = get_option( 'dba_luxstrat_settings', array() );
        if ( empty( $settings['enabled'] ) ) {
            error_log( 'LuxStrat: Module disabled. Skipping.' );
            return;
        }

        try {
            $tz  = new DateTimeZone( self::TIMEZONE );
            $now = new DateTime( 'now', $tz );
        } catch ( \Exception $e ) {
            $now = new DateTime( 'now' );
        }

        $lock_key = 'dba_luxstrat_ran_' . $now->format( 'Y-m-d' );
        if ( get_transient( $lock_key ) ) {
            error_log( 'LuxStrat: Already ran today. Skipping.' );
            return;
        }

        set_transient( $lock_key, true, DAY_IN_SECONDS );

        error_log( 'LuxStrat Scheduler: ' . $now->format( 'l H:i' ) . ' CET — starting generation.' );

        // v2.15.0: Global conflict lock — prevents overlap with ALL modules
        if ( class_exists( 'DBA_Conflict_Center' ) && ! DBA_Conflict_Center::acquire( 'luxstrat', self::CRON_HOOK ) ) {
            delete_transient( $lock_key ); // Let catch-up retry later
            error_log( 'LuxStrat: Another module running — rescheduled +10min' );
            return;
        }

        $generator = DBA_LuxStrat_Generator::get_instance();
        $result    = $generator->run();

        if ( is_wp_error( $result ) ) {
            error_log( 'LuxStrat Scheduler: Error — ' . $result->get_error_message() );
            delete_transient( $lock_key );
        } else {
            error_log( 'LuxStrat Scheduler: Success — Post #' . $result['post_id'] . ' "' . $result['title'] . '" (' . $result['words'] . ' words)' );
        }

        // v2.15.0: Release global lock
        if ( class_exists( 'DBA_Conflict_Center' ) ) DBA_Conflict_Center::release( 'luxstrat' );
    }

    /* ──────────────────────────────────────────────
     *  ADMIN INFO
     * ────────────────────────────────────────────── */

    public static function get_schedule_info() {
        try {
            $tz  = new DateTimeZone( self::TIMEZONE );
            $now = new DateTime( 'now', $tz );
        } catch ( \Exception $e ) {
            return array(
                'next_run'    => 'Error calculating',
                'timezone'    => self::TIMEZONE,
                'frequency'   => 'Every Sunday at 05:00',
                'cron_active' => false,
            );
        }

        $ts = wp_next_scheduled( self::CRON_HOOK );
        if ( $ts ) {
            $next_dt = new DateTime( '@' . $ts );
            $next_dt->setTimezone( $tz );
            $next_run = $next_dt->format( 'l, j F Y \a\t H:i' ) . ' (' . self::TIMEZONE . ')';
        } else {
            $next_run = '⚠️ NOT SCHEDULED — visit admin to auto-fix';
        }

        return array(
            'next_run'     => $next_run,
            'timezone'     => self::TIMEZONE,
            'frequency'    => 'Every Sunday at 05:00 (self-healing)',
            'cron_active'  => (bool) $ts,
            'lock_active'  => (bool) get_transient( 'dba_luxstrat_ran_' . $now->format( 'Y-m-d' ) ),
            'catchup_note' => 'If WP-Cron misses the 05:00 window, the first page load on Sunday will trigger generation automatically.',
        );
    }

    /**
     * Manual trigger (admin "Run Now" button).
     */
    public function manual_run() {
        $settings = get_option( 'dba_luxstrat_settings', array() );
        if ( empty( $settings['enabled'] ) ) {
            return new WP_Error( 'disabled', 'LuxStrat module is disabled. Enable it first.' );
        }

        error_log( 'LuxStrat: Manual run triggered (force mode).' );

        $generator = DBA_LuxStrat_Generator::get_instance();
        return $generator->run( true );
    }
}
