<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Worker\Monitoring;

final class WorkerQuarantineManager
{
    private static ?self $instance = null;

    private WorkerQuarantineStore $store;

    private function __construct(
        private readonly string $basePath,
        private readonly WorkerQuarantinePolicy $policy = new WorkerQuarantinePolicy()
    ) {
        $this->store = new WorkerQuarantineStore($basePath);
    }

    public static function instance(?string $basePath = null): self
    {
        if (self::$instance === null) {
            $basePath ??= self::detectBasePath();
            self::$instance = new self($basePath);
        }

        return self::$instance;
    }

    public static function resetInstance(?string $basePath = null, ?WorkerQuarantinePolicy $policy = null): self
    {
        $basePath ??= self::detectBasePath();
        self::$instance = new self($basePath, $policy ?? WorkerQuarantinePolicy::default());

        return self::$instance;
    }

    public function quarantineFromRecovery(?WorkerRecoveryReport $recoveryReport = null): WorkerQuarantineReport
    {
        if ($recoveryReport === null) {
            $recoveryReport = WorkerRecoveryManager::instance($this->basePath)->load();

            if ($recoveryReport->actions() === []) {
                $recoveryReport = WorkerRecoveryManager::instance($this->basePath)->recover();
            }
        }

        $records = $this->load()->records();
        $indexed = [];

        foreach ($records as $record) {
            $rec = WorkerQuarantineRecord::fromArray($record);
            $indexed[$rec->workerId()] = $rec;
        }

        $processed = 0;

        foreach ($recoveryReport->actions() as $action) {
            if ($processed >= $this->policy->maxQuarantinePerRun()) {
                break;
            }

            $workerId = (string) ($action['workerId'] ?? '');
            $classification = (string) ($action['classification'] ?? '');
            $sourceAction = (string) ($action['action'] ?? '');

            if ($workerId === '' || !$this->policy->shouldQuarantine($classification, $sourceAction)) {
                continue;
            }

            $indexed[$workerId] = WorkerQuarantineRecord::fromRecoveryAction($action);
            $processed++;
        }

        $report = new WorkerQuarantineReport($this->recordsToArray($indexed), $this->statistics($indexed), $this->policy->toArray(), date('c'));
        $this->store->save($report);

        return $report;
    }

    public function release(string $workerId): bool
    {
        $report = $this->load();
        $records = [];
        $released = false;

        foreach ($report->records() as $record) {
            $rec = WorkerQuarantineRecord::fromArray($record);

            if ($rec->workerId() === $workerId && $rec->isActive()) {
                $rec = $rec->release();
                $released = true;
            }

            $records[$rec->workerId()] = $rec;
        }

        $newReport = new WorkerQuarantineReport($this->recordsToArray($records), $this->statistics($records), $report->policy(), date('c'));
        $this->store->save($newReport);

        return $released;
    }

    public function clear(): void
    {
        $this->store->reset();
    }

    public function store(): WorkerQuarantineStore
    {
        return $this->store;
    }

    public function load(): WorkerQuarantineReport
    {
        return $this->store->load();
    }

    public function summary(): array
    {
        $report = $this->store->exists() ? $this->load() : $this->quarantineFromRecovery();

        return [
            'records' => $report->records(),
            'statistics' => $report->statistics(),
            'policy' => $report->policy(),
            'persisted' => $this->store->exists(),
            'stateFile' => $this->store->stateFile(),
        ];
    }

    private function recordsToArray(array $records): array
    {
        return array_map(static fn (WorkerQuarantineRecord $record): array => $record->toArray(), array_values($records));
    }

    private function statistics(array $records): array
    {
        $stats = [
            'records' => count($records),
            'active' => 0,
            'released' => 0,
        ];

        foreach ($records as $record) {
            if (!$record instanceof WorkerQuarantineRecord) {
                continue;
            }

            if ($record->isActive()) {
                $stats['active']++;
            } else {
                $stats['released']++;
            }
        }

        return $stats;
    }

    private static function detectBasePath(): string
    {
        $candidate = dirname(__DIR__, 5);
        $real = realpath($candidate);

        return $real !== false ? $real : $candidate;
    }
}
