<?php

declare(strict_types=1);

namespace App\Framework\Discovery\Results;

use App\Framework\Discovery\ValueObjects\TemplateMapping;
use Countable;

/**
 * Memory-optimized registry for template discoveries using Value Objects
 * Pure Value Object implementation without legacy array support
 */
final class TemplateRegistry implements Countable
{
    /** @var TemplateMapping[] */
    private array $templates = [];

    /** @var array<string, TemplateMapping> */
    private array $templatesByName = [];

    private bool $isOptimized = false;

    public function __construct()
    {
    }

    /**
     * Convert to array for cache serialization
     * @return array<string, mixed>
     */
    public function toArray(): array
    {
        $serializedTemplates = [];
        foreach ($this->templates as $template) {
            $serializedTemplates[] = $template->toArray();
        }

        return [
            'templates' => $serializedTemplates,
        ];
    }

    /**
     * Create TemplateRegistry from array data (for cache deserialization)
     * Always loads as non-optimized to ensure data integrity
     */
    public static function fromArray(array $data): self
    {
        $registry = new self();

        $templates = [];
        foreach (($data['templates'] ?? []) as $templateArray) {
            try {
                $templates[] = TemplateMapping::fromArray($templateArray);
            } catch (\InvalidArgumentException $e) {
                // Skip invalid template mappings from corrupted cache data
                continue;
            }
        }

        $registry->templates = $templates;
        $registry->templatesByName = []; // Don't restore cache, will be rebuilt
        $registry->isOptimized = false; // Always force re-optimization for cache data integrity

        return $registry;
    }

    /**
     * Get all templates as Value Objects
     * @return TemplateMapping[]
     */
    public function getAll(): array
    {
        return $this->templates;
    }

    public function get(string $name): ?TemplateMapping
    {
        if (! isset($this->templatesByName[$name])) {
            foreach ($this->templates as $template) {
                if ($template->name === $name) {
                    $this->templatesByName[$name] = $template;

                    break;
                }
            }
        }

        return $this->templatesByName[$name] ?? null;
    }

    public function has(string $name): bool
    {
        return $this->get($name) !== null;
    }

    public function add(TemplateMapping $template): void
    {
        $this->templates[] = $template;
        $this->templatesByName = [];
        $this->isOptimized = false;
    }

    /**
     * Count total templates (Countable interface)
     */
    public function count(): int
    {
        return count($this->templates);
    }

    public function optimize(): void
    {
        if ($this->isOptimized) {
            return;
        }

        // Deduplicate templates using Value Object unique IDs
        $seen = [];
        $deduplicated = [];

        foreach ($this->templates as $template) {
            $uniqueId = $template->getUniqueId();

            if (! isset($seen[$uniqueId])) {
                $seen[$uniqueId] = true;
                $deduplicated[] = $template;
            }
        }

        $this->templates = $deduplicated;
        $this->templatesByName = [];
        $this->isOptimized = true;
    }

    public function clearCache(): void
    {
        $this->templatesByName = [];
    }

    public function getMemoryStats(): array
    {
        return [
            'templates' => count($this->templates),
            'estimated_bytes' => count($this->templates) * 150,
            'is_optimized' => $this->isOptimized,
        ];
    }

    public function merge(self $other): self
    {
        $merged = new self();

        foreach ($this->templates as $template) {
            $merged->add($template);
        }

        foreach ($other->templates as $template) {
            $merged->add($template);
        }

        $merged->optimize();

        return $merged;
    }
}
