<?php

declare(strict_types=1);

final class MyronDesignLibraryEntitlementPolicy
{
    /**
     * @var array<string, string>
     */
    private array $templateBundleById = [];

    public function __construct(
        private readonly MyronEntitlementPresentationPolicy $entitlements,
        private readonly MyronSiteTypeRegistryRepository $siteTypes,
        private readonly MyronTemplateDefinitionRepository $templates,
    ) {
    }

    public function isBundleAvailable(string $bundleId): bool
    {
        $bundleId = trim($bundleId);
        if ($bundleId === '') {
            return false;
        }

        return isset($this->availableBundleIdMap()[$bundleId]);
    }

    public function isFooterVariantAvailable(string $variantId): bool
    {
        $variantId = trim($variantId);
        if ($variantId === '') {
            return false;
        }

        return isset($this->availableFooterVariantMap()[$variantId]);
    }

    public function assertFooterVariantAvailable(string $variantId): void
    {
        if ($this->isFooterVariantAvailable($variantId)) {
            return;
        }

        throw new RuntimeException('This footer variant is not available for the current license.');
    }

    /**
     * @param list<array<string, mixed>> $rows
     * @return list<array<string, mixed>>
     */
    public function filterFooterRows(array $rows): array
    {
        return array_values(array_filter(
            $rows,
            fn (array $row): bool => $this->isFooterVariantAvailable((string) ($row['variant_id'] ?? ''))
        ));
    }

    /**
     * @param list<array<string, mixed>> $rows
     * @param array<string, list<array<string, mixed>>> $usageIndex
     * @return list<array<string, mixed>>
     */
    public function filterSectionRows(array $rows, array $usageIndex = []): array
    {
        $availableBundles = $this->availableBundleIdMap();
        $filteredRows = [];
        foreach ($rows as $row) {
            $sectionKey = trim((string) ($row['sectionKey'] ?? ''));
            $customMeta = is_array($row['_custom'] ?? null) ? $row['_custom'] : [];
            if ($customMeta !== [] || (string) ($row['jtbdGroup'] ?? '') === 'custom') {
                $filteredRows[] = $row;
                continue;
            }

            $usageRows = is_array($row['usages'] ?? null) ? $row['usages'] : [];
            if ($usageRows === [] && $sectionKey !== '' && is_array($usageIndex[$sectionKey] ?? null)) {
                $usageRows = $usageIndex[$sectionKey];
            }

            $entitledUsageRows = $this->filterSectionUsageRowsAgainstBundles($usageRows, $availableBundles);
            if ($entitledUsageRows === []) {
                continue;
            }

            if (isset($row['usages'])) {
                $row['usages'] = $entitledUsageRows;
                $row['usageCount'] = count($entitledUsageRows);
                $row['sourceTemplateIds'] = $this->sourceTemplateIdsForUsages($entitledUsageRows);
                $row['runtimeTemplateIds'] = $this->runtimeTemplateIdsForUsages($entitledUsageRows);
            }

            $filteredRows[] = $row;
        }

        return $filteredRows;
    }

    /**
     * @param array<string, list<array<string, mixed>>> $usageIndex
     * @return array<string, list<array<string, mixed>>>
     */
    public function filterSectionUsageIndex(array $usageIndex): array
    {
        $availableBundles = $this->availableBundleIdMap();
        $filtered = [];
        foreach ($usageIndex as $sectionKey => $usageRows) {
            $filtered[$sectionKey] = $this->filterSectionUsageRowsAgainstBundles(is_array($usageRows) ? $usageRows : [], $availableBundles);
        }

        return $filtered;
    }

    /**
     * @param list<array<string, mixed>> $usageRows
     * @return list<array<string, mixed>>
     */
    public function filterSectionUsageRows(array $usageRows): array
    {
        return $this->filterSectionUsageRowsAgainstBundles($usageRows, $this->availableBundleIdMap());
    }

    /**
     * @param list<array<string, mixed>> $usageRows
     * @param array<string, bool> $availableBundles
     * @return list<array<string, mixed>>
     */
    private function filterSectionUsageRowsAgainstBundles(array $usageRows, array $availableBundles): array
    {
        $filtered = [];
        foreach ($usageRows as $usage) {
            if (!is_array($usage)) {
                continue;
            }

            $bundleId = trim((string) ($usage['bundleId'] ?? ''));
            if ($bundleId === '') {
                $templateId = trim((string) ($usage['sourceTemplateId'] ?? $usage['templateId'] ?? ''));
                $bundleId = $this->bundleIdForTemplate($templateId);
            }

            if (isset($availableBundles[$bundleId])) {
                $filtered[] = $usage;
            }
        }

        return $filtered;
    }

    /**
     * @return array<string, bool>
     */
    private function availableBundleIdMap(): array
    {
        $available = [
            'site-type.universal' => true,
            'starter.default' => true,
        ];

        foreach ($this->siteTypes->listSiteTypes() as $siteType) {
            if ($this->entitlements->allowsSiteType($siteType)) {
                $bundleId = trim((string) ($siteType['id'] ?? ''));
                if ($bundleId !== '') {
                    $available[$bundleId] = true;
                }
            }
        }

        return $available;
    }

    /**
     * @return array<string, bool>
     */
    private function availableFooterVariantMap(): array
    {
        $variants = [
            MyronFooterDesignRepository::DEFAULT_VARIANT => true,
        ];

        foreach ($this->siteTypes->listSiteTypes() as $siteType) {
            if (!$this->entitlements->allowsSiteType($siteType)) {
                continue;
            }

            $variantId = trim((string) ($siteType['footerVariant'] ?? ''));
            if ($variantId !== '') {
                $variants[$variantId] = true;
            }
        }

        return $variants;
    }

    private function bundleIdForTemplate(string $templateId): string
    {
        $templateId = trim($templateId);
        if ($templateId === '') {
            return '';
        }

        if ($this->templateBundleById === []) {
            foreach ($this->templates->listTemplates() as $template) {
                $id = trim((string) ($template['id'] ?? ''));
                $bundleId = trim((string) ($template['bundleId'] ?? ''));
                if ($id !== '' && $bundleId !== '') {
                    $this->templateBundleById[$id] = $bundleId;
                }
            }
        }

        return (string) ($this->templateBundleById[$templateId] ?? '');
    }

    /**
     * @param list<array<string, mixed>> $usageRows
     * @return list<string>
     */
    private function sourceTemplateIdsForUsages(array $usageRows): array
    {
        $ids = [];
        foreach ($usageRows as $usage) {
            $id = trim((string) ($usage['sourceTemplateId'] ?? $usage['templateId'] ?? ''));
            if ($id !== '') {
                $ids[$id] = true;
            }
        }

        return array_keys($ids);
    }

    /**
     * @param list<array<string, mixed>> $usageRows
     * @return list<string>
     */
    private function runtimeTemplateIdsForUsages(array $usageRows): array
    {
        $ids = [];
        foreach ($usageRows as $usage) {
            $id = trim((string) ($usage['runtimeTemplateId'] ?? ''));
            if ($id !== '') {
                $ids[$id] = true;
            }
        }

        return array_keys($ids);
    }
}
