<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Streaming\Topic;

final class StreamTopicRegistry
{
    /** @var array<string, StreamTopicMetadata> */
    private array $topics = [];
    /** @var array<string, StreamTopicPartition> */
    private array $partitions = [];

    public static function fromArray(array $topics, array $partitions = []): self
    {
        $registry = new self();

        foreach ($topics as $topic) {
            if (is_array($topic)) {
                $registry->put(StreamTopicMetadata::fromArray($topic));
            }
        }

        foreach ($partitions as $partition) {
            if (is_array($partition)) {
                $part = StreamTopicPartition::fromArray($partition);
                $registry->partitions[$part->partitionId()] = $part;
            }
        }

        return $registry;
    }

    public function create(string $topic, int $priority = 0, int $partitions = 1, array $headers = []): StreamTopicMetadata
    {
        if (!isset($this->topics[$topic])) {
            $this->topics[$topic] = StreamTopicMetadata::create($topic, $priority, $partitions, $headers);
            $this->createPartitions($topic, $partitions);
        }

        return $this->topics[$topic];
    }

    public function put(StreamTopicMetadata $topic): void
    {
        $this->topics[$topic->topic()] = $topic;
    }

    public function has(string $topic): bool
    {
        return isset($this->topics[$topic]);
    }

    public function pause(string $topic): bool
    {
        if (!isset($this->topics[$topic])) {
            return false;
        }

        $this->topics[$topic] = $this->topics[$topic]->pause();
        return true;
    }

    public function resume(string $topic): bool
    {
        if (!isset($this->topics[$topic])) {
            return false;
        }

        $this->topics[$topic] = $this->topics[$topic]->resume();
        return true;
    }

    public function archive(string $topic): bool
    {
        if (!isset($this->topics[$topic])) {
            return false;
        }

        $this->topics[$topic] = $this->topics[$topic]->archive();
        return true;
    }

    public function all(): array
    {
        return array_values($this->topics);
    }

    public function partitions(): array
    {
        return array_values($this->partitions);
    }

    public function count(): int
    {
        return count($this->topics);
    }

    public function statistics(): array
    {
        $stats = [
            'topics' => count($this->topics),
            'active' => 0,
            'paused' => 0,
            'archived' => 0,
            'partitions' => count($this->partitions),
        ];

        foreach ($this->topics as $topic) {
            $status = $topic->status();
            if (array_key_exists($status, $stats)) {
                $stats[$status]++;
            }
        }

        return $stats;
    }

    public function toArray(): array
    {
        return array_map(static fn (StreamTopicMetadata $topic): array => $topic->toArray(), $this->all());
    }

    public function partitionsToArray(): array
    {
        return array_map(static fn (StreamTopicPartition $partition): array => $partition->toArray(), $this->partitions());
    }

    private function createPartitions(string $topic, int $count): void
    {
        for ($i = 0; $i < max(1, $count); $i++) {
            $partition = StreamTopicPartition::create($topic, $i);
            $this->partitions[$partition->partitionId()] = $partition;
        }
    }
}
