<?php

namespace WPSN\Modules\Diagnostics;

use WPSN\Core\Container;
use WPSN\Core\Http\HttpClient;
use WPSN\Core\Logger;
use WPSN\Core\Settings;
use WPSN\Modules\AI\SaaSAICreditsClient;
use WPSN\Modules\Licensing\BillingCenterData;
use WPSN\Modules\Licensing\BillingSyncService;
use WPSN\Modules\Licensing\LicenseService;
use WPSN\Modules\Licensing\SelfServeManagementService;
use WPSN\Modules\Licensing\SelfServeUpgradeService;
use WPSN\Modules\SaaSDashboard\SaaSDashboardService;
use WPSN\Modules\SaaSSync\SaaSSyncService;

defined('ABSPATH') || exit;

/**
 * Probes the optional external platform services used by the plugin.
 *
 * Goal: give operators a single read-mostly snapshot for optional backends
 * like license verification, billing sync, signed updates, SaaS policy sync,
 * AI proxy credits and worker endpoint readiness.
 */
final class PlatformOpsProbeService
{
    private Container $container;
    private Settings $settings;
    private Logger $logger;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->settings = $container->get('settings');
        $this->logger = $container->get('logger');
    }

    /**
     * @param array<string,mixed> $context
     * @return array<string,mixed>
     */
    public function run(array $context = []): array
    {
        $started = microtime(true);

        $checks = [
            'license_verify' => $this->probeLicenseVerify(),
            'signed_updates' => $this->probeSignedUpdates(),
            'billing_sync' => $this->probeBillingSync(),
            'self_serve_catalog' => $this->probeSelfServeCatalog(),
            'self_serve_management' => $this->probeSelfServeManagement(),
            'saas_policy' => $this->probeSaaSPolicy(),
            'saas_dashboard_health' => $this->probeSaaSDashboardHealth(),
            'ai_proxy_credits' => $this->probeAiProxyCredits(),
            'crawl_workers' => $this->probeCrawlWorkersReadiness(),
            'clustering_workers' => $this->probeClusteringWorkersReadiness(),
        ];

        $counts = [
            'ok' => 0,
            'warn' => 0,
            'critical' => 0,
            'skipped' => 0,
        ];

        foreach ($checks as $check) {
            if (!is_array($check)) {
                continue;
            }
            $severity = isset($check['severity']) ? (string) $check['severity'] : 'info';
            if ($severity === 'skipped' || (isset($check['status']) && (string) $check['status'] === 'skipped')) {
                $counts['skipped']++;
                continue;
            }
            if (isset($counts[$severity])) {
                $counts[$severity]++;
            }
        }

        $overall = 'ok';
        if ($counts['critical'] > 0) {
            $overall = 'critical';
        } elseif ($counts['warn'] > 0) {
            $overall = 'warn';
        } elseif ($counts['ok'] === 0 && $counts['skipped'] > 0) {
            $overall = 'info';
        }

        return [
            'id' => function_exists('wp_generate_uuid4') ? wp_generate_uuid4() : md5((string) microtime(true)),
            'created_at_utc' => gmdate('c'),
            'plugin_version' => defined('WPSN_VERSION') ? (string) WPSN_VERSION : '',
            'build' => defined('WPSN_BUILD') ? (string) WPSN_BUILD : '',
            'site_url' => (string) home_url('/'),
            'local_only' => $this->isLocalOnly(),
            'duration_ms' => (int) round((microtime(true) - $started) * 1000),
            'severity' => $overall,
            'counts' => $counts,
            'summary' => [
                'ok' => $counts['ok'],
                'warn' => $counts['warn'],
                'critical' => $counts['critical'],
                'skipped' => $counts['skipped'],
                'live_external_allowed' => !$this->isLocalOnly(),
            ],
            'context' => $context,
            'checks' => $checks,
        ];
    }

    /**
     * @return array<string,mixed>
     */
    private function probeLicenseVerify(): array
    {
        $check = $this->baseCheck(__('License verification backend', 'wp-seo-next'));
        $service = new LicenseService($this->settings, $this->logger);

        $serverUrl = $service->serverUrl();
        $hasKey = $service->hasLicenseKey();
        $check['details'] = [
            'server' => $this->sanitizeEndpoint($serverUrl),
            'has_license_key' => $hasKey,
            'require_signed' => $service->requireSigned(),
            'status_cached' => $service->status(),
            'trusted_cached' => $service->isTrusted(),
        ];

        if ($serverUrl === '') {
            return $this->skipped($check, __('License server URL is not configured.', 'wp-seo-next'));
        }
        if (!$hasKey) {
            return $this->skipped($check, __('License key is not configured.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks external license verification.', 'wp-seo-next'));
        }

        $res = $service->verifyIfNeeded(true);
        $status = $service->status();
        $cache = $service->getCache();
        $license = is_array($cache) && isset($cache['license']) && is_array($cache['license']) ? $cache['license'] : [];
        $plan = isset($license['plan']) ? (string) $license['plan'] : '';
        $expiresAt = isset($license['expires_at']) ? (string) $license['expires_at'] : '';
        $entitlements = $service->entitlements();

        $check['details']['status_effective'] = $status;
        $check['details']['trusted'] = $service->isTrusted();
        $check['details']['plan'] = $plan;
        $check['details']['expires_at'] = $expiresAt;
        $check['details']['entitlements_count'] = count($entitlements);
        $check['details']['entitlements'] = array_slice($entitlements, 0, 20);
        $check['http_status'] = isset($res['http_status']) ? (int) $res['http_status'] : 0;

        if (!empty($res['ok'])) {
            if (in_array($status, [LicenseService::STATUS_ACTIVE, LicenseService::STATUS_GRACE], true)) {
                return $this->ok($check, $status === LicenseService::STATUS_ACTIVE
                    ? __('License verified and active.', 'wp-seo-next')
                    : __('License verification is in grace mode.', 'wp-seo-next'));
            }

            return $this->warn($check, sprintf(
                /* translators: %s is effective license status. */
                __('License verified, but effective status is %s.', 'wp-seo-next'),
                $status !== '' ? $status : 'unknown'
            ));
        }

        $error = isset($res['error']) ? (string) $res['error'] : __('License verification failed.', 'wp-seo-next');
        $check['details']['retry_after'] = isset($res['retry_after']) ? (int) $res['retry_after'] : 0;
        return $this->critical($check, $error);
    }

    /**
     * @return array<string,mixed>
     */
    private function probeSignedUpdates(): array
    {
        $check = $this->baseCheck(__('Signed update backend', 'wp-seo-next'));
        $service = new LicenseService($this->settings, $this->logger);

        $enabled = (bool) $this->settings->get('licensing.enable_updates', false);
        $serverUrl = trim((string) $this->settings->get('licensing.update_server_url', ''));
        if ($serverUrl === '') {
            $serverUrl = $service->serverUrl();
        }
        $path = trim((string) $this->settings->get('licensing.update_path', '/wp-seo-next/plugin/update'));
        if ($path === '') {
            $path = '/wp-seo-next/plugin/update';
        }
        $endpoint = $serverUrl !== '' ? rtrim($serverUrl, '/') . '/' . ltrim($path, '/') : '';

        $check['details'] = [
            'enabled' => $enabled,
            'server' => $this->sanitizeEndpoint($endpoint),
            'license_status' => $service->status(),
            'trusted_license' => $service->isTrusted(),
            'require_signed' => $service->requireSigned(),
        ];

        if (!$enabled) {
            return $this->skipped($check, __('Signed updates are disabled in settings.', 'wp-seo-next'));
        }
        if ($endpoint === '') {
            return $this->warn($check, __('Update endpoint is not configured.', 'wp-seo-next'));
        }
        if (!$service->hasLicenseKey()) {
            return $this->warn($check, __('Cannot probe updates without a license key.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks external update checks.', 'wp-seo-next'));
        }

        $payload = [
            'license_key' => $service->getLicenseKey(),
            'site_url' => home_url('/'),
            'plugin_version' => defined('WPSN_VERSION') ? (string) WPSN_VERSION : '',
            'build' => defined('WPSN_BUILD') ? (string) WPSN_BUILD : 'free',
            'ts' => time(),
        ];

        $res = $this->postJson($endpoint, $payload, 'licensing', 'update_check', 15, [
            'X-WPSN-Service' => 'licensing',
            'X-WPSN-Context' => 'update_check',
        ]);
        $check['http_status'] = (int) ($res['http_status'] ?? 0);

        if (!$res['ok']) {
            return $this->critical($check, (string) ($res['error'] ?? __('Update check failed.', 'wp-seo-next')));
        }

        $data = isset($res['data']) && is_array($res['data']) ? $res['data'] : [];
        if (empty($data['ok'])) {
            $check['details']['response_ok'] = false;
            return $this->warn($check, isset($data['error']) ? (string) $data['error'] : __('Update backend returned ok=false.', 'wp-seo-next'));
        }

        $check['details']['response_ok'] = true;
        $check['details']['remote_version'] = isset($data['version']) ? (string) $data['version'] : '';
        $check['details']['download_host'] = isset($data['download_url']) ? $this->sanitizeEndpoint((string) $data['download_url']) : '';
        $check['details']['tested'] = isset($data['tested']) ? (string) $data['tested'] : '';
        $check['details']['requires_php'] = isset($data['requires_php']) ? (string) $data['requires_php'] : '';

        return $this->ok($check, __('Update backend responded with a signed update payload.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeBillingSync(): array
    {
        $check = $this->baseCheck(__('Billing sync backend', 'wp-seo-next'));
        $service = new BillingSyncService($this->settings, $this->logger);
        $license = new LicenseService($this->settings, $this->logger);

        $check['details'] = [
            'enabled' => $service->isEnabled(),
            'server' => $this->sanitizeEndpoint($service->serverUrl()),
            'has_license_key' => $service->hasLicenseKey(),
            'require_signed' => $service->requireSigned(),
        ];

        if (!$service->isEnabled()) {
            return $this->skipped($check, __('Billing sync is disabled in settings.', 'wp-seo-next'));
        }
        if ($service->serverUrl() === '') {
            return $this->warn($check, __('Billing sync server URL is not configured.', 'wp-seo-next'));
        }
        if (!$service->hasLicenseKey()) {
            return $this->warn($check, __('Billing sync needs a license key.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks billing sync requests.', 'wp-seo-next'));
        }

        $res = $service->syncIfNeeded(true);
        $cache = $service->getCache();
        $billing = BillingCenterData::build($license->getCache(), $this->settings->all(), $cache);
        $check['details']['plan'] = isset($billing['plan']) ? (string) $billing['plan'] : '';
        $check['details']['seat_used'] = isset($billing['seat_used']) ? (int) $billing['seat_used'] : null;
        $check['details']['seat_limit'] = isset($billing['seat_limit']) ? (int) $billing['seat_limit'] : null;
        $check['details']['site_count'] = isset($billing['site_count']) ? (int) $billing['site_count'] : null;
        $check['details']['site_limit'] = isset($billing['site_limit']) ? (int) $billing['site_limit'] : null;
        $check['details']['invoices_count'] = isset($billing['invoices']) && is_array($billing['invoices']) ? count($billing['invoices']) : 0;
        $check['details']['trusted'] = is_array($cache) ? !empty($cache['trusted']) : false;
        $check['details']['source'] = isset($billing['source']) ? (string) $billing['source'] : '';

        if (!empty($res['ok'])) {
            if ($service->requireSigned() && empty($check['details']['trusted'])) {
                return $this->warn($check, __('Billing sync succeeded, but payload is not trusted.', 'wp-seo-next'));
            }
            return $this->ok($check, __('Billing sync returned account and seat data.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('Billing sync failed.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeSelfServeCatalog(): array
    {
        $check = $this->baseCheck(__('Self-serve catalog backend', 'wp-seo-next'));
        $service = new SelfServeUpgradeService($this->settings, $this->logger);

        $check['details'] = [
            'enabled' => $service->isEnabled(),
            'has_license_key' => $service->hasLicenseKey(),
            'require_signed' => $service->requireSigned(),
        ];

        if (!$service->isEnabled()) {
            return $this->skipped($check, __('Self-serve catalog is disabled.', 'wp-seo-next'));
        }
        if (!$service->hasLicenseKey()) {
            return $this->warn($check, __('Self-serve catalog requires a license key.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks self-serve catalog requests.', 'wp-seo-next'));
        }

        $res = $service->fetchCatalogIfNeeded(true);
        $catalog = [];
        if (!empty($res['catalog']) && is_array($res['catalog'])) {
            $catalog = $res['catalog'];
        } else {
            $cache = $service->getCatalogCache();
            if (is_array($cache) && isset($cache['catalog']) && is_array($cache['catalog'])) {
                $catalog = $cache['catalog'];
            }
        }

        $plans = isset($catalog['plans']) && is_array($catalog['plans']) ? $catalog['plans'] : [];
        $check['details']['plans_count'] = count($plans);
        $check['details']['current_plan'] = isset($catalog['current_plan']) ? (string) $catalog['current_plan'] : '';
        $check['details']['trusted'] = isset($res['trusted']) ? (bool) $res['trusted'] : false;

        if (!empty($res['ok'])) {
            if ($service->requireSigned() && empty($check['details']['trusted'])) {
                return $this->warn($check, __('Self-serve catalog loaded, but payload is not trusted.', 'wp-seo-next'));
            }
            return $this->ok($check, __('Self-serve catalog returned plan data.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('Self-serve catalog failed.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeSelfServeManagement(): array
    {
        $check = $this->baseCheck(__('Self-serve management backend', 'wp-seo-next'));
        $service = new SelfServeManagementService($this->settings, $this->logger);

        $check['details'] = [
            'enabled' => $service->isEnabled(),
            'has_license_key' => $service->hasLicenseKey(),
            'require_signed' => $service->requireSigned(),
        ];

        if (!$service->isEnabled()) {
            return $this->skipped($check, __('Self-serve management is disabled.', 'wp-seo-next'));
        }
        if (!$service->hasLicenseKey()) {
            return $this->warn($check, __('Self-serve management requires a license key.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks self-serve management requests.', 'wp-seo-next'));
        }

        $res = $service->fetchStateIfNeeded(true);
        $management = isset($res['management']) && is_array($res['management']) ? $res['management'] : [];
        $subscription = isset($management['subscription']) && is_array($management['subscription']) ? $management['subscription'] : [];
        $sites = $management['sites'] ?? ($management['site_assignments'] ?? []);
        $seats = $management['seats'] ?? ($management['seat_assignments'] ?? []);
        $invoices = $management['invoices'] ?? [];

        $check['details']['trusted'] = isset($res['trusted']) ? (bool) $res['trusted'] : false;
        $check['details']['subscription_status'] = isset($subscription['status']) ? (string) $subscription['status'] : '';
        $check['details']['plan_id'] = isset($subscription['plan_id']) ? (string) $subscription['plan_id'] : (isset($management['current_plan']) ? (string) $management['current_plan'] : '');
        $check['details']['sites_count'] = is_array($sites) ? count($sites) : 0;
        $check['details']['seats_count'] = is_array($seats) ? count($seats) : 0;
        $check['details']['invoices_count'] = is_array($invoices) ? count($invoices) : 0;

        if (!empty($res['ok'])) {
            if ($service->requireSigned() && empty($check['details']['trusted'])) {
                return $this->warn($check, __('Self-serve management loaded, but payload is not trusted.', 'wp-seo-next'));
            }
            return $this->ok($check, __('Self-serve management returned subscription, seats and invoices.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('Self-serve management failed.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeSaaSPolicy(): array
    {
        $check = $this->baseCheck(__('SaaS policy / orchestration backend', 'wp-seo-next'));
        $service = new SaaSSyncService($this->settings, $this->logger);

        $check['details'] = [
            'enabled' => $service->isEnabled(),
            'server' => $this->sanitizeEndpoint($service->serverUrl()),
            'has_auth_token' => $service->hasAuthToken(),
            'require_signed' => $service->requireSigned(),
            'status_cached' => $service->status(),
            'trusted_cached' => $service->isTrusted(),
        ];

        if (!$service->isEnabled()) {
            return $this->skipped($check, __('SaaS sync is disabled.', 'wp-seo-next'));
        }
        if ($service->serverUrl() === '') {
            return $this->warn($check, __('SaaS server URL is not configured.', 'wp-seo-next'));
        }
        if (!$service->hasAuthToken()) {
            return $this->warn($check, __('SaaS auth token is not configured.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks SaaS policy sync.', 'wp-seo-next'));
        }

        $res = $service->pullIfNeeded(true);
        $payload = $service->payload();
        $check['details']['status_effective'] = $service->status();
        $check['details']['trusted'] = $service->isTrusted();
        $check['details']['entitlements_count'] = count($service->entitlements());
        $check['details']['features_count'] = count($service->features());
        $check['details']['endpoints_count'] = isset($payload['endpoints']) && is_array($payload['endpoints']) ? count($payload['endpoints']) : 0;

        if (!empty($res['ok'])) {
            if ($service->requireSigned() && !$service->isTrusted()) {
                return $this->warn($check, __('SaaS policy sync succeeded, but payload is not trusted.', 'wp-seo-next'));
            }
            if (!in_array($service->status(), [SaaSSyncService::STATUS_ACTIVE, SaaSSyncService::STATUS_GRACE], true)) {
                return $this->warn($check, __('SaaS policy sync succeeded, but effective status is not active.', 'wp-seo-next'));
            }
            return $this->ok($check, __('SaaS policy sync returned endpoints and entitlements.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('SaaS policy sync failed.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeSaaSDashboardHealth(): array
    {
        $check = $this->baseCheck(__('SaaS dashboard health backend', 'wp-seo-next'));
        $service = new SaaSDashboardService($this->settings, $this->logger);
        $saas = new SaaSSyncService($this->settings, $this->logger);

        $path = $service->endpointPath('health');
        $check['details'] = [
            'enabled' => $service->isEnabled(),
            'server' => $this->sanitizeEndpoint($saas->endpoint('dashboard_health', $path)),
            'has_auth_token' => $saas->hasAuthToken(),
            'require_signed_tasks' => $service->requireSignedTasks(),
        ];

        if (!$service->isEnabled()) {
            return $this->skipped($check, __('SaaS dashboard sync is disabled.', 'wp-seo-next'));
        }
        if (!$saas->hasAuthToken()) {
            return $this->warn($check, __('SaaS dashboard health requires an auth token.', 'wp-seo-next'));
        }
        if ($path === '') {
            return $this->warn($check, __('SaaS dashboard health path is not configured.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks SaaS dashboard health push.', 'wp-seo-next'));
        }

        $res = $service->pushHealth();
        $check['http_status'] = isset($res['code']) ? (int) $res['code'] : 0;
        if (!empty($res['ok'])) {
            return $this->ok($check, __('SaaS dashboard health endpoint accepted the site snapshot.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('SaaS dashboard health push failed.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeAiProxyCredits(): array
    {
        $check = $this->baseCheck(__('AI proxy credits backend', 'wp-seo-next'));
        $provider = (string) $this->settings->get('ai.external.provider', 'openai');
        $saas = new SaaSSyncService($this->settings, $this->logger);

        $check['details'] = [
            'provider' => $provider,
            'enabled' => $provider === 'saas_proxy',
            'server' => $this->sanitizeEndpoint($saas->endpoint('ai_credits', (string) $this->settings->get('saas_sync.ai_credits_path', '/wp-seo-next/ai/credits'))),
            'has_auth_token' => $saas->hasAuthToken(),
        ];

        if ($provider !== 'saas_proxy') {
            return $this->skipped($check, __('AI provider is not set to SaaS proxy.', 'wp-seo-next'));
        }
        if (!$saas->hasAuthToken()) {
            return $this->warn($check, __('SaaS AI proxy requires an auth token.', 'wp-seo-next'));
        }
        if ($this->isLocalOnly()) {
            return $this->skipped($check, __('Local-only mode blocks AI proxy credit checks.', 'wp-seo-next'));
        }

        $client = new SaaSAICreditsClient($saas, $this->logger, $this->settings);
        $res = $client->fetch();
        $check['http_status'] = isset($res['http_status']) ? (int) $res['http_status'] : 0;
        $check['details']['balance'] = isset($res['balance']) ? (float) $res['balance'] : 0.0;
        $check['details']['currency'] = isset($res['currency']) ? (string) $res['currency'] : 'credits';

        if (!empty($res['ok'])) {
            return $this->ok($check, __('AI proxy credits endpoint returned a balance snapshot.', 'wp-seo-next'));
        }

        return $this->critical($check, isset($res['error']) ? (string) $res['error'] : __('AI proxy credits check failed.', 'wp-seo-next'));
    }

    /**
     * Crawl workers are intentionally passive: we do not start a live crawl from a probe run.
     *
     * @return array<string,mixed>
     */
    private function probeCrawlWorkersReadiness(): array
    {
        $check = $this->baseCheck(__('Crawl workers readiness', 'wp-seo-next'), 'passive');
        $saas = new SaaSSyncService($this->settings, $this->logger);
        $endpoint = $saas->endpoint('crawl', (string) $this->settings->get('saas_sync.crawl_path', '/wp-seo-next/crawl'));

        $check['details'] = [
            'enabled' => (bool) $this->settings->moduleEnabled('site_crawl', true),
            'server' => $this->sanitizeEndpoint($endpoint),
            'saas_active' => $saas->isActive(),
            'saas_trusted' => $saas->isTrusted(),
            'has_auth_token' => $saas->hasAuthToken(),
            'live_ping' => false,
        ];

        if (!$this->settings->moduleEnabled('site_crawl', true)) {
            return $this->skipped($check, __('Site Crawl module is disabled.', 'wp-seo-next'));
        }
        if ($endpoint === '') {
            return $this->warn($check, __('Crawl endpoint is not configured.', 'wp-seo-next'));
        }
        if (!$saas->hasAuthToken()) {
            return $this->warn($check, __('Crawl workers need a SaaS auth token.', 'wp-seo-next'));
        }
        if (!$saas->isActive()) {
            return $this->warn($check, __('SaaS policy is not active yet, so crawl workers are not ready.', 'wp-seo-next'));
        }
        if ($saas->requireSigned() && !$saas->isTrusted()) {
            return $this->warn($check, __('Crawl endpoint is configured, but current SaaS policy is not trusted.', 'wp-seo-next'));
        }

        return $this->ok($check, __('Crawl workers are configured and ready. Live crawl start is intentionally not triggered by this probe.', 'wp-seo-next'));
    }

    /**
     * @return array<string,mixed>
     */
    private function probeClusteringWorkersReadiness(): array
    {
        $check = $this->baseCheck(__('Clustering workers readiness', 'wp-seo-next'), 'passive');
        $saas = new SaaSSyncService($this->settings, $this->logger);
        $endpoint = $saas->endpoint('clustering', (string) $this->settings->get('saas_sync.clustering_path', '/wp-seo-next/clustering/build'));

        $check['details'] = [
            'enabled' => (bool) $this->settings->moduleEnabled('content_clusters', true),
            'server' => $this->sanitizeEndpoint($endpoint),
            'saas_active' => $saas->isActive(),
            'saas_trusted' => $saas->isTrusted(),
            'has_auth_token' => $saas->hasAuthToken(),
            'live_ping' => false,
        ];

        if (!$this->settings->moduleEnabled('content_clusters', true)) {
            return $this->skipped($check, __('Content Clusters module is disabled.', 'wp-seo-next'));
        }
        if ($endpoint === '') {
            return $this->warn($check, __('Clustering endpoint is not configured.', 'wp-seo-next'));
        }
        if (!$saas->hasAuthToken()) {
            return $this->warn($check, __('Clustering workers need a SaaS auth token.', 'wp-seo-next'));
        }
        if (!$saas->isActive()) {
            return $this->warn($check, __('SaaS policy is not active yet, so clustering workers are not ready.', 'wp-seo-next'));
        }
        if ($saas->requireSigned() && !$saas->isTrusted()) {
            return $this->warn($check, __('Clustering endpoint is configured, but current SaaS policy is not trusted.', 'wp-seo-next'));
        }

        return $this->ok($check, __('Clustering workers are configured and ready. Live clustering is intentionally not triggered by this probe.', 'wp-seo-next'));
    }

    private function isLocalOnly(): bool
    {
        return (bool) $this->settings->get('privacy.local_only', false);
    }

    /**
     * @return array<string,mixed>
     */
    private function baseCheck(string $label, string $mode = 'live'): array
    {
        return [
            'label' => $label,
            'mode' => $mode,
            'ok' => false,
            'severity' => 'info',
            'status' => 'pending',
            'summary' => '',
            'http_status' => 0,
            'details' => [],
        ];
    }

    /**
     * @param array<string,mixed> $check
     * @return array<string,mixed>
     */
    private function ok(array $check, string $summary): array
    {
        $check['ok'] = true;
        $check['severity'] = 'ok';
        $check['status'] = 'ok';
        $check['summary'] = $summary;
        return $check;
    }

    /**
     * @param array<string,mixed> $check
     * @return array<string,mixed>
     */
    private function warn(array $check, string $summary): array
    {
        $check['ok'] = false;
        $check['severity'] = 'warn';
        $check['status'] = 'warn';
        $check['summary'] = $summary;
        return $check;
    }

    /**
     * @param array<string,mixed> $check
     * @return array<string,mixed>
     */
    private function critical(array $check, string $summary): array
    {
        $check['ok'] = false;
        $check['severity'] = 'critical';
        $check['status'] = 'critical';
        $check['summary'] = $summary;
        return $check;
    }

    /**
     * @param array<string,mixed> $check
     * @return array<string,mixed>
     */
    private function skipped(array $check, string $summary): array
    {
        $check['ok'] = false;
        $check['severity'] = 'skipped';
        $check['status'] = 'skipped';
        $check['summary'] = $summary;
        return $check;
    }

    private function sanitizeEndpoint(string $url): string
    {
        $url = trim($url);
        if ($url === '') {
            return '';
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts)) {
            return preg_replace('#\?.*$#', '', $url) ?: $url;
        }

        $scheme = isset($parts['scheme']) ? (string) $parts['scheme'] . '://' : '';
        $host = isset($parts['host']) ? (string) $parts['host'] : '';
        $port = isset($parts['port']) ? ':' . (string) $parts['port'] : '';
        $path = isset($parts['path']) ? (string) $parts['path'] : '';

        return $scheme . $host . $port . $path;
    }

    /**
     * @param array<string,mixed> $payload
     * @param array<string,string> $extraHeaders
     * @return array{ok:bool,http_status:int,error:string,data:array<string,mixed>,retry_after:int}
     */
    private function postJson(string $endpoint, array $payload, string $service, string $context, int $timeout = 15, array $extraHeaders = []): array
    {
        $headers = array_merge([
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
            'User-Agent' => 'WP SEO Next/' . (defined('WPSN_VERSION') ? (string) WPSN_VERSION : 'dev') . '; ' . home_url('/'),
        ], $extraHeaders);

        $res = HttpClient::post($endpoint, [
            'timeout' => max(3, min(60, $timeout)),
            'headers' => $headers,
            'body' => wp_json_encode($payload),
        ], [
            'service' => $service,
            'context' => $context,
            'retries' => 1,
            'retry_statuses' => [429, 500, 502, 503, 504],
        ]);

        if (is_wp_error($res)) {
            $retryAfter = 0;
            $data = $res->get_error_data();
            if (is_array($data) && isset($data['retry_after'])) {
                $retryAfter = (int) $data['retry_after'];
            }
            return [
                'ok' => false,
                'http_status' => 0,
                'error' => $res->get_error_message(),
                'data' => [],
                'retry_after' => $retryAfter,
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($res);
        $body = (string) wp_remote_retrieve_body($res);
        $decoded = $body !== '' ? json_decode($body, true) : [];
        if (!is_array($decoded)) {
            $decoded = [];
        }

        $retryAfter = 0;
        $hdr = wp_remote_retrieve_header($res, 'retry-after');
        if (is_string($hdr) && $hdr !== '' && is_numeric($hdr)) {
            $retryAfter = (int) $hdr;
        }

        if ($code >= 200 && $code < 300) {
            return [
                'ok' => true,
                'http_status' => $code,
                'error' => '',
                'data' => $decoded,
                'retry_after' => $retryAfter,
            ];
        }

        $err = isset($decoded['error']) ? (string) $decoded['error'] : ($body !== '' ? $body : ('HTTP ' . $code));
        return [
            'ok' => false,
            'http_status' => $code,
            'error' => $err,
            'data' => $decoded,
            'retry_after' => $retryAfter,
        ];
    }
}
