<?php

namespace SoftminalB2B\Services;

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

/**
 * Recurring background work, on Action Scheduler (ARCHITECTURE §4).
 *
 * Action Scheduler ships inside WooCommerce and, unlike raw WP-Cron, does not
 * stall on low-traffic stores — which is exactly the kind of store this plugin
 * serves. The money-critical Pro jobs (statements, reminders, quote expiry)
 * register through this same service in Phase 2; Free ships one housekeeping
 * job plus the health data the dashboard shows.
 *
 * The group `smb2b` tags every action so uninstall can cancel them all at once.
 */
class Scheduler
{
    public const GROUP           = 'smb2b';
    public const HOOK_HOUSEKEEPING = 'smb2b_daily_housekeeping';

    private static bool $registered = false;

    public static function init(): void
    {
        if (self::$registered) {
            return;
        }

        self::$registered = true;

        add_action(self::HOOK_HOUSEKEEPING, [self::class, 'runHousekeeping']);
        add_action('init', [self::class, 'ensureScheduled'], 20);
    }

    /**
     * Queue the recurring jobs once. Safe to call on every load — Action
     * Scheduler dedupes by hook, so this never stacks duplicates.
     */
    public static function ensureScheduled(): void
    {
        if (!self::available()) {
            return;
        }

        if (!as_has_scheduled_action(self::HOOK_HOUSEKEEPING, [], self::GROUP)) {
            as_schedule_recurring_action(
                time() + HOUR_IN_SECONDS,
                DAY_IN_SECONDS,
                self::HOOK_HOUSEKEEPING,
                [],
                self::GROUP
            );
        }
    }

    public static function available(): bool
    {
        return function_exists('as_schedule_recurring_action')
            && function_exists('as_has_scheduled_action');
    }

    /**
     * Daily tidy-up. Free's only job: remove temp invoice PDFs that were
     * written for email attachment and never cleaned up, plus the dompdf font
     * cache if it has grown stale.
     */
    public static function runHousekeeping(): void
    {
        self::purgeTempInvoices();

        update_option('smb2b_last_housekeeping', current_time('mysql'), false);

        do_action('smb2b_housekeeping');
    }

    private static function purgeTempInvoices(): void
    {
        $dir = get_temp_dir();

        $files = glob(trailingslashit($dir) . 'invoice-*.pdf') ?: [];
        $cutoff = time() - HOUR_IN_SECONDS;

        foreach ($files as $file) {
            // Only our attachment temp files, and only once they are stale, so a
            // send in progress is never pulled out from under itself.
            if (is_file($file) && filemtime($file) < $cutoff) {
                wp_delete_file($file);
            }
        }
    }

    /**
     * What the dashboard health panel shows.
     *
     * @return array<string, mixed>
     */
    public static function health(): array
    {
        $available = self::available();

        $jobs = [];

        if ($available) {
            $jobs[] = [
                'hook'     => self::HOOK_HOUSEKEEPING,
                'label'    => __('Daily cleanup', 'softminal-b2b-wholesale-for-woocommerce'),
                'scheduled' => as_has_scheduled_action(self::HOOK_HOUSEKEEPING, [], self::GROUP),
                'next_run' => self::nextRun(self::HOOK_HOUSEKEEPING),
            ];
        }

        return [
            'action_scheduler' => $available,
            'last_housekeeping' => (string) get_option('smb2b_last_housekeeping', ''),
            'jobs'             => $jobs,
        ];
    }

    private static function nextRun(string $hook): ?string
    {
        if (!function_exists('as_next_scheduled_action')) {
            return null;
        }

        $timestamp = as_next_scheduled_action($hook, [], self::GROUP);

        if (!is_int($timestamp) || $timestamp <= 0) {
            return null;
        }

        return wp_date('Y-m-d H:i', $timestamp);
    }
}
