<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Distributed\Failover\Validation;

use PpPhp\Core\Platform\Event\Distributed\DistributedRuntimeCoordinator;
use PpPhp\Core\Platform\Event\Distributed\Lease\DistributedLeaseManager;
use PpPhp\Core\Platform\Event\Distributed\Queue\DistributedQueueCoordinator;
use PpPhp\Core\Platform\Event\Distributed\Failover\DistributedFailoverManager;
use PpPhp\Core\Platform\Event\Distributed\Failover\DistributedFailoverPolicy;

final class DistributedFailoverVerifier
{
    public function verify(string $basePath): array
    {
        $runtime = DistributedRuntimeCoordinator::resetInstance($basePath);
        $runtime->clear();
        $runtime->registerWorker('failover_worker_healthy', 'failover_node_healthy', 2);
        $runtime->registerWorker('failover_worker_failed', 'failover_node_failed', 1);

        $runtimeFile = $runtime->store()->stateFile();
        $runtimeData = json_decode((string) file_get_contents($runtimeFile), true);

        foreach ($runtimeData['workers'] as &$worker) {
            if (($worker['nodeId'] ?? '') === 'failover_node_failed') {
                $worker['lastSeenAt'] = date('c', time() - 120);
            }
        }
        unset($worker);

        file_put_contents($runtimeFile, json_encode($runtimeData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
        $runtime->hydrate();

        $leases = DistributedLeaseManager::resetInstance($basePath);
        $leases->clear();
        $leases->acquire('failover_job_1', 'failover_node_failed', 'failover_worker_failed', 1);
        sleep(2);

        /*
         * Do not pre-expire the lease here.
         * The failover manager owns the recovery pass and should expire/recover
         * leases during inspect(). Pre-expiring in the verifier made the second
         * expire pass correctly return 0 and caused a false negative.
         */

        $queues = DistributedQueueCoordinator::resetInstance($basePath);
        $queues->clear();
        $queues->registerQueue('failover.high', 10);
        $queues->registerQueue('failover.low', 1);
        $queues->updateCounters('failover.high', 20, 0);
        $queues->updateCounters('failover.low', 2, 0);

        $manager = DistributedFailoverManager::resetInstance($basePath, new DistributedFailoverPolicy(30, true, true, 50));
        $manager->clear();

        $report = $manager->inspect();
        $reportData = $report->toArray();
        $stats = $report->statistics();

        $leaseExpired = (int) ($reportData['leaseRecovery']['expired'] ?? 0);
        $leaseRecovered = (int) ($reportData['leaseRecovery']['recovered'] ?? 0);

        $checks = [
            'nodeDetection' => ($stats['failed'] ?? 0) === 1,
            'leaseRecovery' => ($leaseExpired >= 1 || $leaseRecovered >= 1),
            'queueRecovery' => (($reportData['queueRecovery']['balanced'] ?? false) === true),
            'actions' => ($stats['actions'] ?? 0) >= 1,
            'persistence' => $manager->store()->exists(),
        ];

        $issues = [];
        foreach ($checks as $check => $ok) {
            if (!$ok) {
                $issues[] = ['check' => $check, 'message' => 'Distributed failover verification failed: ' . $check];
            }
        }

        return [
            'ok' => !in_array(false, $checks, true),
            'checks' => $checks,
            'issues' => $issues,
            'summary' => [
                'nodes' => $stats['nodes'] ?? 0,
                'healthy' => $stats['healthy'] ?? 0,
                'failed' => $stats['failed'] ?? 0,
                'actions' => $stats['actions'] ?? 0,
                'leaseExpired' => $leaseExpired,
                'leaseRecovered' => $leaseRecovered,
                'score' => in_array(false, $checks, true) ? 80 : 100,
            ],
        ];
    }
}
