<?php

declare(strict_types=1);

namespace App\Framework\Discovery\Results;

use Countable;

/**
 * Central registry that coordinates the smaller specialized registries
 * This replaces the monolithic DiscoveryResults with a composition of lightweight registries
 *
 * MEMORY OPTIMIZATION: Implements __serialize/__unserialize to prevent cache memory explosion
 */
final readonly class DiscoveryRegistry implements Countable
{
    public function __construct(
        public AttributeRegistry $attributes = new AttributeRegistry(),
        public InterfaceRegistry $interfaces = new InterfaceRegistry(),
        public TemplateRegistry $templates = new TemplateRegistry(),
    ) {
    }

    // === Memory Management ===

    /**
     * Optimize all registries for memory efficiency
     */
    public function optimize(): void
    {
        $this->attributes->optimize();
        $this->interfaces->optimize();
        $this->templates->optimize();
    }

    /**
     * Clear all caches to free memory
     */
    public function clearCaches(): void
    {
        $this->attributes->clearCache();
        $this->interfaces->clearCache();
        $this->templates->clearCache();
    }

    /**
     * Get comprehensive memory statistics
     */
    public function getMemoryStats(): array
    {
        $attributeStats = $this->attributes->getMemoryStats();
        $interfaceStats = $this->interfaces->getMemoryStats();
        $templateStats = $this->templates->getMemoryStats();

        return [
            'total_estimated_bytes' => $attributeStats['estimated_bytes'] + $interfaceStats['estimated_bytes'] + $templateStats['estimated_bytes'],
            'attributes' => $attributeStats,
            'interfaces' => $interfaceStats,
            'templates' => $templateStats,
        ];
    }

    public function isEmpty(): bool
    {
        return count($this) === 0;
    }

    public function count(): int
    {
        return count($this->attributes) +
               count($this->interfaces) +
               count($this->templates);
    }

    /**
     * Create a lightweight version with only essential data
     */
    public function createLightweight(): self
    {
        // Keep only commonly accessed attributes
        $essentialAttributes = new AttributeRegistry();
        $importantTypes = [
            'App\\Framework\\Attributes\\Route',
            'App\\Framework\\DI\\Initializer',
            'App\\Framework\\Core\\Events\\OnEvent',
            'App\\Framework\\Http\\MiddlewarePriorityAttribute',
        ];

        foreach ($importantTypes as $type) {
            if ($this->attributes->has($type)) {
                foreach ($this->attributes->get($type) as $data) {
                    $essentialAttributes->add($type, $data);
                }
            }
        }

        return new self(
            attributes: $essentialAttributes,
            interfaces: $this->interfaces,
            templates: new TemplateRegistry() // Skip templates in lightweight version
        );
    }

    // === Factory Methods ===

    public static function empty(): self
    {
        return new self();
    }

    /**
     * Convert to array for cache serialization
     */
    public function toArray(): array
    {
        return [
            'attributes' => $this->attributes->toArray(),
            'interfaces' => $this->interfaces->toArray(),
            'templates' => $this->templates->toArray(),
        ];
    }

    public function __serialize(): array
    {
        return $this->toArray();
    }

    /**
     * Custom deserialization using fromArray() method
     */
    public function __unserialize(array $data): void
    {
        $restored = self::fromArray($data);
        $this->attributes = $restored->attributes;
        $this->interfaces = $restored->interfaces;
        $this->templates = $restored->templates;
    }

    /**
     * Create DiscoveryRegistry from array data (for cache deserialization)
     * Uses direct constructor instantiation with data injection
     */
    public static function fromArray(array $data): self
    {
        // Verwende die fromArray Factory-Methoden der Registry-Klassen
        // Diese laden automatisch als nicht-optimiert für Datenintegrität
        return new self(
            attributes: isset($data['attributes'])
                ? AttributeRegistry::fromArray($data['attributes'])
                : new AttributeRegistry(),
            interfaces: isset($data['interfaces'])
                ? InterfaceRegistry::fromArray($data['interfaces'])
                : new InterfaceRegistry(),
            templates: isset($data['templates'])
                ? TemplateRegistry::fromArray($data['templates'])
                : new TemplateRegistry()
        );
    }

    /**
     * Merge multiple registries efficiently
     */
    public function merge(self $other): self
    {
        $mergedAttributes = new AttributeRegistry();

        // Copy all attributes from both registries
        foreach ($this->attributes->getAllTypes() as $type) {
            foreach ($this->attributes->get($type) as $mapping) {
                $mergedAttributes->add($type, $mapping);
            }
        }

        foreach ($other->attributes->getAllTypes() as $type) {
            foreach ($other->attributes->get($type) as $mapping) {
                $mergedAttributes->add($type, $mapping);
            }
        }

        $merged = new self(
            attributes: $mergedAttributes,
            interfaces: $this->interfaces->merge($other->interfaces),
            templates: $this->templates->merge($other->templates)
        );

        $merged->optimize();

        return $merged;
    }

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

    /**
     * Get the attributes registry
     */
    public function attributes(): AttributeRegistry
    {
        return $this->attributes;
    }

    /**
     * Filter registry by critical attributes (for performance optimization)
     * @param array<class-string> $criticalAttributes
     */
    public function filterByCriticalAttributes(array $criticalAttributes): self
    {
        $filteredAttributes = new AttributeRegistry();

        foreach ($criticalAttributes as $attributeClass) {
            $items = $this->attributes->get($attributeClass);
            if (! empty($items)) {
                foreach ($items as $item) {
                    $filteredAttributes->add($attributeClass, $item);
                }
            }
        }

        return new self($filteredAttributes, $this->interfaces, $this->templates);
    }

    /**
     * Get difference between two registries (for lazy loading)
     */
    public function diff(self $other): self
    {
        $diffAttributes = new AttributeRegistry();

        // Find attributes that are in this registry but not in the other
        foreach ($this->attributes->getAllTypes() as $attributeClass) {
            $thisItems = $this->attributes->get($attributeClass);
            $otherItems = $other->attributes->get($attributeClass);

            foreach ($thisItems as $item) {
                if (! in_array($item, $otherItems, true)) {
                    $diffAttributes->add($attributeClass, $item);
                }
            }
        }

        return new self($diffAttributes, $this->interfaces, $this->templates);
    }
}
