<?php
/**
 * License Heartbeat (WP Cron).
 *
 * Calls the server's /v1/auth/heartbeat on a schedule to keep the
 * license marked as active and to update last-seen timestamps.
 *
 * Runs every 12 hours via WP Cron. If the server returns inactive,
 * the client marks the license as inactive locally too.
 *
 * @package SwapAds\Client\License
 * @since   1.0.0
 */

declare(strict_types=1);

namespace SwapAds\Client\License;

use SwapAds\Client\Api\RestClient;
use SwapAds\Client\License\LicenseManager;

/**
 * Class LicenseHeartbeat.
 *
 * @since 1.0.0
 */
final class LicenseHeartbeat
{
    public const HOOK_NAME       = 'swapads_client_license_heartbeat';
    public const SCHEDULE_NAME   = 'twicedaily';
    public const OPTION_KEY      = 'swapads_client_last_heartbeat_at';

    /**
     * Register WP Cron hook + schedule.
     *
     * @since 1.0.0
     */
    public static function register(): void
    {
        if (!function_exists('add_action')) {
            return;
        }
        add_action(self::HOOK_NAME, [self::class, 'run']);
        if (!wp_next_scheduled(self::HOOK_NAME)) {
            wp_schedule_event(time(), self::SCHEDULE_NAME, self::HOOK_NAME);
        }
    }

    /**
     * Deregister WP Cron hook + clear schedule.
     *
     * @since 1.0.0
     */
    public static function deregister(): void
    {
        $timestamp = wp_next_scheduled(self::HOOK_NAME);
        if ($timestamp) {
            wp_unschedule_event($timestamp, self::HOOK_NAME);
        }
    }

    /**
     * Run the heartbeat: call /v1/auth/heartbeat and update local state.
     *
     * @return array<string, mixed> Result {success, status, error?}
     *
     * @since 1.0.0
     */
    public static function run(): array
    {
        if (!LicenseManager::isLicensed()) {
            return ['success' => false, 'error' => 'NOT_LICENSED', 'status' => 'inactive'];
        }
        $serverUrl = (string) get_option('swapads_client_server_url', '');
        $licenseKey = (string) LicenseManager::key();
        $secret = (string) LicenseManager::secret();
        $siteId = (int) LicenseManager::siteId();

        if ($serverUrl === '' || $licenseKey === '' || $secret === '') {
            return ['success' => false, 'error' => 'INCOMPLETE_CONFIG', 'status' => 'inactive'];
        }

        try {
            $client = new RestClient($serverUrl);
            $response = $client->post('/v1/auth/heartbeat', [
                'license_key' => $licenseKey,
                'site_id'     => $siteId,
            ]);
            $data = $response['data'] ?? [];
            $status = (string) ($data['status'] ?? 'unknown');
            update_option(self::OPTION_KEY, time());
            if ($status === 'inactive') {
                LicenseManager::store($licenseKey, $secret, $siteId, (string) LicenseManager::domain(), 'inactive');
            } else {
                LicenseManager::store($licenseKey, $secret, $siteId, (string) LicenseManager::domain(), 'active');
            }
            return ['success' => true, 'status' => $status];
        } catch (\Throwable $e) {
            return ['success' => false, 'error' => 'REQUEST_FAILED', 'message' => $e->getMessage()];
        }
    }

    /**
     * Get timestamp of last successful heartbeat (or 0).
     *
     * @return int Unix timestamp.
     *
     * @since 1.0.0
     */
    public static function lastHeartbeatAt(): int
    {
        return (int) get_option(self::OPTION_KEY, 0);
    }
}
