<?php

declare(strict_types=1);

final class MyronLiveSectionInventoryReadModel
{
    public function __construct(
        private readonly MyronContentRepository $content,
        private readonly MyronSectionRegistryRepository $sections,
        private readonly MyronSectionComponentDefaultsRepository $defaults,
        private readonly MyronSectionContentDefaultsRepository $sectionDefaults,
    ) {
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    public function inventoryIndex(): array
    {
        $templatesById = $this->runtimeTemplatesById();
        $sectionKeyByComponentId = $this->sectionKeyByPrimaryComponentId();
        $sectionsByKey = $this->sectionsByKey();
        $index = [];

        foreach ($this->content->listPages() as $page) {
            $runtimeTemplateId = trim((string) ($page['template_id'] ?? ''));
            $runtimeTemplate = $templatesById[$runtimeTemplateId] ?? null;
            if (!is_array($runtimeTemplate)) {
                continue;
            }

            $sourceTemplateId = $this->sourceTemplateIdForPage($page, $runtimeTemplate);
            if ($sourceTemplateId === '') {
                continue;
            }

            $schema = is_array($runtimeTemplate['schema'] ?? null) ? $runtimeTemplate['schema'] : [];
            $schemaSections = array_values(array_filter(
                is_array($schema['sections'] ?? null) ? $schema['sections'] : [],
                static fn (mixed $section): bool => is_array($section)
            ));

            foreach ($schemaSections as $position => $section) {
                $componentId = MyronSectionComponentDefaultsRepository::normalizeComponentId((string) ($section['componentId'] ?? ''));
                if ($componentId === '' || in_array($componentId, ['header', 'footer'], true)) {
                    continue;
                }

                $variant = MyronSectionComponentDefaultsRepository::normalizeVariant((string) ($section['variant'] ?? ''));
                $identityKey = MyronSectionComponentDefaultsRepository::identityKey($componentId, $variant);
                $declaredSectionKey = trim((string) ($section['sectionKey'] ?? ''));
                $inferredSectionKey = $sectionKeyByComponentId[$componentId] ?? '';
                $sectionKey = $declaredSectionKey !== '' ? $declaredSectionKey : $inferredSectionKey;
                $governedSection = $sectionKey !== '' ? ($sectionsByKey[$sectionKey] ?? null) : null;
                $componentDefault = $this->defaults->findDefault($componentId, $variant);
                $sectionDefault = $sectionKey !== '' ? $this->sectionDefaults->findDefault($sectionKey, $componentId, $variant) : null;

                if (!isset($index[$identityKey])) {
                    $index[$identityKey] = $this->createInventoryRow(
                        $identityKey,
                        $componentId,
                        $variant,
                        $sectionKey,
                        $declaredSectionKey !== '' ? 'declared' : ($inferredSectionKey !== '' ? 'primaryComponent' : ''),
                        is_array($governedSection) ? $governedSection : null,
                        is_array($componentDefault) ? $componentDefault : null,
                        is_array($sectionDefault) ? $sectionDefault : null
                    );
                }

                $index[$identityKey]['usageCount']++;
                $index[$identityKey]['sourceTemplateIds'][$sourceTemplateId] = $sourceTemplateId;
                $index[$identityKey]['runtimeTemplateIds'][$runtimeTemplateId] = $runtimeTemplateId;
                $index[$identityKey]['pages'][] = [
                    'id' => (string) ($page['id'] ?? ''),
                    'title' => (string) ($page['title'] ?? ''),
                    'path' => (string) ($page['path'] ?? ''),
                    'publicationState' => (string) ($page['publication_state'] ?? ''),
                ];
                $index[$identityKey]['usages'][] = [
                    'pageId' => (string) ($page['id'] ?? ''),
                    'pagePath' => (string) ($page['path'] ?? ''),
                    'pageTitle' => (string) ($page['title'] ?? ''),
                    'sourceTemplateId' => $sourceTemplateId,
                    'runtimeTemplateId' => $runtimeTemplateId,
                    'position' => (int) $position,
                    'componentId' => $componentId,
                    'variant' => $variant,
                    'sectionKey' => $sectionKey,
                    'matchedBy' => $declaredSectionKey !== '' ? 'sectionKey' : ($inferredSectionKey !== '' ? 'primaryComponent' : 'componentId'),
                ];
            }
        }

        foreach ($index as &$row) {
            $row['sourceTemplateIds'] = array_values($row['sourceTemplateIds']);
            $row['runtimeTemplateIds'] = array_values($row['runtimeTemplateIds']);
        }
        unset($row);

        uasort(
            $index,
            static fn (array $a, array $b): int => [(string) ($a['label'] ?? ''), (string) ($a['identityKey'] ?? '')] <=> [(string) ($b['label'] ?? ''), (string) ($b['identityKey'] ?? '')]
        );

        return $index;
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function inventoryRows(): array
    {
        return array_values($this->inventoryIndex());
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findByIdentity(string $componentId, string $variant = ''): ?array
    {
        $identityKey = MyronSectionComponentDefaultsRepository::identityKey($componentId, $variant);

        return $this->inventoryIndex()[$identityKey] ?? null;
    }

    /**
     * @return list<string>
     */
    public function identityKeys(): array
    {
        return array_keys($this->inventoryIndex());
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function runtimeTemplatesById(): array
    {
        $templates = [];
        foreach ($this->content->listTemplates() as $template) {
            $templateId = trim((string) ($template['id'] ?? ''));
            if ($templateId !== '') {
                $templates[$templateId] = $template;
            }
        }

        return $templates;
    }

    /**
     * @param array<string, mixed>|null $runtimeTemplate
     */
    private function sourceTemplateIdForPage(array $page, ?array $runtimeTemplate): string
    {
        $pageSourceTemplateId = trim((string) ($page['source_template_id'] ?? ''));
        if ($pageSourceTemplateId !== '') {
            return $pageSourceTemplateId;
        }

        return is_array($runtimeTemplate) ? trim((string) ($runtimeTemplate['source_template_id'] ?? '')) : '';
    }

    /**
     * @return array<string, array<string, mixed>>
     */
    private function sectionsByKey(): array
    {
        $sections = [];
        foreach ($this->sections->listSections() as $section) {
            $sectionKey = trim((string) ($section['sectionKey'] ?? ''));
            if ($sectionKey !== '') {
                $sections[$sectionKey] = $section;
            }
        }

        return $sections;
    }

    /**
     * @return array<string, string>
     */
    private function sectionKeyByPrimaryComponentId(): array
    {
        $index = [];
        foreach ($this->sections->listSections() as $section) {
            $sectionKey = trim((string) ($section['sectionKey'] ?? ''));
            if ($sectionKey === '') {
                continue;
            }

            foreach (is_array($section['primaryComponents'] ?? null) ? $section['primaryComponents'] : [] as $componentId) {
                $componentId = trim((string) $componentId);
                if ($componentId !== '' && !isset($index[$componentId])) {
                    $index[$componentId] = $sectionKey;
                }
            }
        }

        return $index;
    }

    /**
     * @param array<string, mixed>|null $governedSection
     * @param array<string, mixed>|null $componentDefault
     * @param array<string, mixed>|null $sectionDefault
     * @return array<string, mixed>
     */
    private function createInventoryRow(
        string $identityKey,
        string $componentId,
        string $variant,
        string $sectionKey,
        string $sectionKeySource,
        ?array $governedSection,
        ?array $componentDefault,
        ?array $sectionDefault
    ): array {
        $defaultSource = is_array($sectionDefault) ? $sectionDefault : (is_array($componentDefault) ? $componentDefault : []);
        $defaultContentEditable = is_array($sectionDefault) && array_key_exists('defaultContentEditable', $sectionDefault)
            ? $sectionDefault['defaultContentEditable']
            : (is_array($componentDefault) && array_key_exists('defaultContentEditable', $componentDefault)
                ? $componentDefault['defaultContentEditable']
                : ($governedSection['defaultContentEditable'] ?? null));

        return [
            'identityKey' => $identityKey,
            'componentId' => $componentId,
            'variant' => $variant,
            'label' => $this->labelFor($componentId, $variant, $governedSection),
            'sectionKey' => $sectionKey,
            'sectionKeySource' => $sectionKeySource,
            'jtbdGroup' => (string) ($governedSection['jtbdGroup'] ?? 'current-site'),
            'sectionType' => (string) ($governedSection['sectionType'] ?? 'rendered'),
            'remediationStatus' => (string) ($governedSection['remediationStatus'] ?? 'live'),
            'defaultContentEditable' => $defaultContentEditable,
            'defaultContent' => is_array($defaultSource['defaultContent'] ?? null) ? $defaultSource['defaultContent'] : [],
            'flags' => is_array($defaultSource['flags'] ?? null) ? $defaultSource['flags'] : [],
            'defaultContentSource' => is_array($sectionDefault) ? 'section-key' : (is_array($componentDefault) ? 'component' : 'registry'),
            'usageCount' => 0,
            'sourceTemplateIds' => [],
            'runtimeTemplateIds' => [],
            'pages' => [],
            'usages' => [],
        ];
    }

    /**
     * @param array<string, mixed>|null $governedSection
     */
    private function labelFor(string $componentId, string $variant, ?array $governedSection): string
    {
        $base = ucwords(str_replace('-', ' ', $componentId));

        return $variant === '' ? $base : $base . ' · ' . ucwords(str_replace('-', ' ', $variant));
    }
}
