<?php

declare(strict_types=1);

namespace app\common\service;

/**
 * Node Puppeteer 服务健康与队列负载探测
 */
class SplitNodeHealthService
{
    /**
     * 拉取 /api/stats，失败返回 null（不阻断同步，由后续请求快速失败）
     *
     * @return array<string, mixed>|null
     */
    public static function fetchStats(): ?array
    {
        $url = SplitSyncConfigService::getNodeHost() . '/api/stats';
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2);
        curl_setopt($ch, CURLOPT_TIMEOUT, 3);
        $response = curl_exec($ch);
        if (curl_errno($ch)) {
            curl_close($ch);
            return null;
        }
        $httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        if ($httpCode !== 200) {
            return null;
        }
        $decoded = json_decode((string) $response, true);
        return is_array($decoded) ? $decoded : null;
    }

    /**
     * 当前是否适合向 Node 提交新 Browser 任务
     *
     * A2C 等 antiBot 任务走 real profile，应参考 queueReal 而非 standard 队列。
     */
    public static function canAcceptWork(): bool
    {
        return self::canAcceptWorkForTicketType('');
    }

    /**
     * 指定工单类型是否可向 Node 提交新任务
     *
     * API 优先类型（如 Whatshub）常态不占 Real Browser，调度层应跳过队列门禁。
     */
    public static function canAcceptWorkForTicketType(string $ticketType): bool
    {
        if ($ticketType !== '' && SplitScrmSpiderFactory::isApiFirstSyncType($ticketType)) {
            return true;
        }

        $stats = self::fetchStats();
        if ($stats === null) {
            return true;
        }
        if (!empty($stats['shuttingDown'])) {
            return false;
        }

        $snapshot = self::resolveQueueSnapshot($stats);
        $threshold = SplitSyncConfigService::getNodeQueueSkipThreshold();
        if ($snapshot['queued'] >= $threshold) {
            return false;
        }

        return $snapshot['active'] < $snapshot['max'];
    }

    /**
     * @return array{queued:int,active:int,max:int,profile:string}
     */
    public static function getQueueSnapshot(): array
    {
        $stats = self::fetchStats();
        if ($stats === null) {
            return ['queued' => 0, 'active' => 0, 'max' => 0, 'profile' => 'real'];
        }

        $snapshot = self::resolveQueueSnapshot($stats);
        return [
            'queued'  => $snapshot['queued'],
            'active'  => $snapshot['active'],
            'max'     => $snapshot['max'],
            'profile' => $snapshot['profile'],
        ];
    }

    /**
     * 优先使用 real 队列（antiBot / A2C），无数据时回退 standard
     *
     * @param array<string, mixed> $stats
     * @return array{queued:int,active:int,max:int,profile:string}
     */
    private static function resolveQueueSnapshot(array $stats): array
    {
        $config = is_array($stats['config'] ?? null) ? $stats['config'] : [];
        $queueReal = is_array($stats['queueReal'] ?? null) ? $stats['queueReal'] : [];
        if ($queueReal !== []) {
            return [
                'queued'  => (int) ($queueReal['queued'] ?? 0),
                'active'  => (int) ($queueReal['active'] ?? 0),
                'max'     => max(1, (int) ($config['maxConcurrentBrowsersReal'] ?? ($queueReal['max'] ?? 2))),
                'profile' => 'real',
            ];
        }

        $queue = is_array($stats['queue'] ?? null) ? $stats['queue'] : [];

        return [
            'queued'  => (int) ($queue['queued'] ?? 0),
            'active'  => (int) ($queue['active'] ?? 0),
            'max'     => max(1, (int) ($config['maxConcurrentBrowsers'] ?? ($queue['max'] ?? 4))),
            'profile' => 'standard',
        ];
    }

    /**
     * Node 侧 Real Browser / Captcha 是否就绪（供同步失败日志可读性）
     *
     * @return array{realBrowserReady:bool,captchaConfigured:bool}
     */
    public static function getAntiBotSnapshot(): array
    {
        $stats = self::fetchStats();
        if ($stats === null) {
            return ['realBrowserReady' => false, 'captchaConfigured' => false];
        }
        return [
            'realBrowserReady'  => !empty($stats['realBrowserReady']),
            'captchaConfigured' => !empty($stats['captchaConfigured']),
        ];
    }
}
