<?php

declare(strict_types=1);

final class MyronEditorSurfaceReadModel
{
    public function __construct(
        private readonly MyronContentRepository $content,
        private readonly MyronTemplateDefinitionRepository $definitions,
        private readonly MyronSectionRegistryRepository $sections,
        private readonly MyronSectionContentSchemaService $schema,
        private readonly ?MyronTemplateContentResolver $contentResolver = null,
    ) {
    }

    /**
     * @return array<string, mixed>|null
     */
    public function pageSurface(string $pageId): ?array
    {
        $page = $this->content->getEditablePage($pageId);
        if (!is_array($page)) {
            return null;
        }

        $schema = is_array($page['template_schema'] ?? null) ? $page['template_schema'] : [];
        $sourceTemplateId = trim((string) (($page['source_template_id'] ?? '') ?: ($schema['sourceTemplateId'] ?? '')));
        $sourceBundleId = trim((string) (($page['source_bundle_id'] ?? '') ?: ($schema['sourceBundleId'] ?? '')));
        $templateKey = trim((string) ($page['template_key'] ?? ''));
        $content = is_array($page['content'] ?? null) ? $page['content'] : [];

        return [
            'kind' => 'page',
            'identity' => [
                'id' => (string) ($page['id'] ?? ''),
                'title' => (string) ($page['title'] ?? ''),
                'path' => (string) ($page['path'] ?? ''),
                'templateId' => (string) ($page['template_id'] ?? ''),
                'templateKey' => $templateKey,
                'templateName' => (string) ($page['template_name'] ?? ''),
                'sourceTemplateId' => $sourceTemplateId,
                'sourceBundleId' => $sourceBundleId,
            ],
            'sourceAuthority' => $sourceTemplateId !== '' ? 'source-linked-runtime-page' : 'operator-owned-page',
            'ownershipState' => $sourceTemplateId !== '' ? 'source-linked-page' : 'page-owned',
            'availableActions' => [
                'editPageInfo',
                'editSeo',
                'publish',
                'manageMenus',
                'inspectSections',
            ],
            'chrome' => is_array($schema['chrome'] ?? null) ? $schema['chrome'] : [],
            'chromeSummary' => $this->chromeSummary($schema),
            'sections' => $this->sectionNodes(
                is_array($schema['sections'] ?? null) ? $schema['sections'] : [],
                $content,
                'page',
                $sourceTemplateId,
                $sourceBundleId
            ),
            'raw' => [
                'page' => $page,
                'schema' => $schema,
            ],
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function templateSurface(string $sourceTemplateId): ?array
    {
        $definition = $this->definitions->findTemplateById($sourceTemplateId);
        $runtimeTemplate = $this->runtimeTemplateForSource($sourceTemplateId);
        $runtimeSchema = is_array($runtimeTemplate['schema'] ?? null) ? $runtimeTemplate['schema'] : null;
        if (!is_array($definition) && is_array($runtimeSchema)) {
            $baseTemplateId = trim((string) ($runtimeSchema['sourceTemplateBaseId'] ?? ''));
            $definition = $baseTemplateId !== '' ? $this->definitions->findTemplateById($baseTemplateId) : null;
        }
        if (!is_array($definition) && is_array($runtimeTemplate)) {
            $definition = [
                'id' => (string) ($runtimeTemplate['source_template_id'] ?? $sourceTemplateId),
                'name' => (string) ($runtimeTemplate['name'] ?? $sourceTemplateId),
                'pageTitle' => (string) ($runtimeTemplate['name'] ?? $sourceTemplateId),
                'bundleId' => (string) ($runtimeTemplate['source_bundle_id'] ?? ''),
                'chrome' => is_array($runtimeSchema['chrome'] ?? null) ? $runtimeSchema['chrome'] : [],
                'sections' => is_array($runtimeSchema['sections'] ?? null) ? $runtimeSchema['sections'] : [],
            ];
        }
        if (!is_array($definition)) {
            return null;
        }

        $definitionSourceId = (string) ($definition['id'] ?? $sourceTemplateId);
        $definitionSchema = [
            'sourceTemplateId' => $definitionSourceId,
            'sourceBundleId' => (string) ($definition['bundleId'] ?? ''),
            'chrome' => MyronTemplateDefinitionRepository::chromeForTemplateRecord($definition),
            'sections' => MyronTemplateDefinitionRepository::bodySectionsForRecord($definition),
        ];
        $runtimeSchema = is_array($runtimeTemplate['schema'] ?? null) ? $runtimeTemplate['schema'] : null;
        $schema = $runtimeSchema ?? $definitionSchema;
        $sourceBundleId = trim((string) (($schema['sourceBundleId'] ?? '') ?: ($definition['bundleId'] ?? '')));
        $runtimeId = trim((string) ($runtimeTemplate['id'] ?? ''));
        $ownershipState = $runtimeId !== '' ? 'runtime-editable-template' : 'packaged-definition';
        $defaultContent = is_array($schema['defaultContent'] ?? null) ? $schema['defaultContent'] : [];

        return [
            'kind' => 'template',
            'identity' => [
                'id' => (string) ($definition['id'] ?? ''),
                'title' => (string) (($definition['pageTitle'] ?? '') ?: ($definition['name'] ?? $sourceTemplateId)),
                'name' => (string) ($definition['name'] ?? $sourceTemplateId),
                'templateClass' => MyronTemplateDefinitionRepository::templateClassForTemplateId((string) (($schema['sourceTemplateBaseId'] ?? '') ?: ($schema['sourceTemplateId'] ?? $sourceTemplateId))),
                'sourceTemplateId' => (string) ($schema['sourceTemplateId'] ?? $definitionSourceId),
                'sourceBundleId' => $sourceBundleId,
                'runtimeTemplateId' => $runtimeId,
                'runtimeTemplateKey' => (string) ($runtimeTemplate['template_key'] ?? ''),
            ],
            'sourceAuthority' => $runtimeId !== '' ? 'runtime-template-draft' : 'packaged-template-definition',
            'ownershipState' => $ownershipState,
            'availableActions' => $runtimeId !== ''
                ? ['inspectTemplateInfo', 'insertSection', 'reorderSections']
                : ['inspectTemplateInfo', 'createRuntimeDraft'],
            'chrome' => is_array($schema['chrome'] ?? null) ? $schema['chrome'] : [],
            'chromeSummary' => $this->chromeSummary($schema),
            'sections' => $this->sectionNodes(
                is_array($schema['sections'] ?? null) ? $schema['sections'] : [],
                $defaultContent,
                $runtimeId !== '' ? 'template-default' : 'template',
                $sourceTemplateId,
                $sourceBundleId
            ),
            'raw' => [
                'definition' => $definition,
                'runtimeTemplate' => $runtimeTemplate,
                'schema' => $schema,
            ],
        ];
    }

    /**
     * @param array<string, mixed> $content
     * @return array<string, mixed>|null
     */
    public function templateStartedPageSurface(string $sourceTemplateId, array $content = []): ?array
    {
        $definition = $this->definitions->findTemplateById($sourceTemplateId);
        if (!is_array($definition)) {
            return null;
        }

        // STN-013-001: Defer defaultContent resolution to the shared MyronTemplateContentResolver.
        // The resolver's contract is "given a templateId, return the canonical defaultContent
        // shape" — runtime template first, platform definition fallback. By delegating here, the
        // page-editor template_source flow stays in lockstep with the Site Templates preview
        // surface and the Page Templates editor surface; the next surface that needs to render
        // template-with-content joins the existing pattern instead of inventing its own.
        $runtimeTemplate = $this->runtimeTemplateForSource($sourceTemplateId);
        $runtimeSchema = is_array($runtimeTemplate['schema'] ?? null) ? $runtimeTemplate['schema'] : null;

        $sourceBundleId = trim((string) (
            (is_array($runtimeSchema) && ($runtimeSchema['sourceBundleId'] ?? '') !== '')
                ? $runtimeSchema['sourceBundleId']
                : ($definition['bundleId'] ?? '')
        ));

        $sections = is_array($runtimeSchema['sections'] ?? null) && $runtimeSchema['sections'] !== []
            ? array_values(array_filter($runtimeSchema['sections'], 'is_array'))
            : MyronTemplateDefinitionRepository::bodySectionsForRecord($definition);

        $chrome = is_array($runtimeSchema['chrome'] ?? null)
            ? $runtimeSchema['chrome']
            : MyronTemplateDefinitionRepository::chromeForTemplateRecord($definition);

        // Resolved seed defaultContent flows in unless the caller passes $content (e.g.,
        // re-rendering after a validation error so unsaved operator edits are not silently lost).
        $resolvedDefaultContent = $this->contentResolver !== null
            ? $this->contentResolver->resolveDefaultContent($sourceTemplateId, false)
            : (is_array($runtimeSchema['defaultContent'] ?? null) ? $runtimeSchema['defaultContent'] : []);
        $effectiveContent = $content !== [] ? $content : $resolvedDefaultContent;

        return [
            'kind' => 'page-draft-from-template',
            'identity' => [
                'id' => '',
                'title' => (string) (($definition['pageTitle'] ?? '') ?: ($definition['name'] ?? $sourceTemplateId)),
                'path' => '',
                'templateId' => '',
                'templateKey' => '',
                'templateName' => (string) ($definition['name'] ?? $sourceTemplateId),
                'sourceTemplateId' => (string) ($definition['id'] ?? $sourceTemplateId),
                'sourceBundleId' => $sourceBundleId,
            ],
            'sourceAuthority' => 'source-template-started-draft',
            'ownershipState' => 'unsaved-page-draft',
            'availableActions' => [
                'editPageInfo',
                'editSeo',
                'saveDraft',
                'inspectSections',
            ],
            'chrome' => $chrome,
            'chromeSummary' => $this->chromeSummary(['chrome' => $chrome]),
            'sections' => $this->sectionNodes($sections, $effectiveContent, 'page', $sourceTemplateId, $sourceBundleId),
            'raw' => [
                'definition' => $definition,
                'runtimeTemplate' => $runtimeTemplate,
                'schema' => [
                    'sourceTemplateId' => (string) ($definition['id'] ?? $sourceTemplateId),
                    'sourceBundleId' => $sourceBundleId,
                    'sections' => $sections,
                    'defaultContent' => $resolvedDefaultContent,
                ],
            ],
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function runtimeTemplateForSource(string $sourceTemplateId): ?array
    {
        return $this->content->findRuntimeTemplateByIdOrSourceTemplateId($sourceTemplateId);
    }

    /**
     * @param list<array<string, mixed>> $sections
     * @param array<string, mixed> $content
     * @return list<array<string, mixed>>
     */
    private function sectionNodes(array $sections, array $content, string $surfaceKind, string $sourceTemplateId, string $sourceBundleId): array
    {
        $nodes = [];
        foreach ($sections as $position => $section) {
            if (!is_array($section)) {
                continue;
            }

            $sectionKey = trim((string) ($section['sectionKey'] ?? ''));
            $componentId = trim((string) ($section['componentId'] ?? ''));
            $identity = $sectionKey !== '' ? $sectionKey : $componentId;
            $instanceKey = myronSectionInstanceKey($section, (int) $position);
            $sectionUuid = myronSectionUuid($section, (int) $position);
            $sectionContent = myronSectionOwnedContentForInstance($content, $section, (int) $position);
            $governedSection = $sectionKey !== '' ? $this->sections->findSectionByKey($sectionKey) : $this->sections->findSectionByKey($componentId);
            $componentRecord = $componentId !== '' ? myronComponentRegistryRepository()->findComponentById($componentId) : null;
            $editableTokens = is_array($section['editableTokens'] ?? null)
                ? $section['editableTokens']
                : (is_array($componentRecord) && is_array($componentRecord['editableTokens'] ?? null) ? $componentRecord['editableTokens'] : []);
            $schemaBackedSurface = in_array($surfaceKind, ['page', 'template-default', 'section-default'], true);
            $contentSchema = $this->schema->schemaForSection($section, (int) $position, $sectionContent, $surfaceKind);
            $editableFields = $schemaBackedSurface ? $contentSchema['fields'] : [];
            $repeatableGroups = $schemaBackedSurface ? $contentSchema['repeaters'] : [];
            $contractedSection = $schemaBackedSurface && (($contentSchema['contracted'] ?? false) === true);
            $explicitEditability = $this->explicitEditabilityForSection($section);
            $defaultEditability = is_array($governedSection) && array_key_exists('defaultContentEditable', $governedSection)
                ? $governedSection['defaultContentEditable']
                : null;
            $isEditable = $schemaBackedSurface
                ? ($explicitEditability ?? ($contractedSection ? true : ($defaultEditability === null ? ($editableFields !== []) : (bool) $defaultEditability)))
                : ($explicitEditability ?? ($defaultEditability === null ? false : (bool) $defaultEditability));
            if (!$isEditable) {
                $editableFields = [];
                $repeatableGroups = [];
            }

            $nodes[] = [
                'id' => 'section-' . ((int) $position + 1) . '-' . myronNormalizeSlug($identity !== '' ? $identity : 'unknown'),
                'instanceKey' => $instanceKey,
                'sectionUuid' => $sectionUuid,
                'position' => (int) $position,
                'label' => is_array($governedSection)
                    ? (string) ($governedSection['label'] ?? $identity)
                    : ucwords(str_replace('-', ' ', $identity !== '' ? $identity : 'Unknown Section')),
                'sectionKey' => $sectionKey,
                'componentId' => $componentId,
                'variant' => (string) ($section['variant'] ?? ''),
                'sourceTemplateId' => $sourceTemplateId,
                'sourceBundleId' => $sourceBundleId,
                'sourceAuthority' => $surfaceKind === 'page' ? 'template-schema' : ($surfaceKind === 'template-default' ? 'runtime-template-default-content' : 'template-composition'),
                'ownershipState' => $isEditable ? ($surfaceKind === 'template-default' ? 'template-owned-default-content' : 'page-owned-content') : 'inspect-only',
                'editability' => $isEditable ? 'editable' : 'inspect-only',
                'editableFields' => $editableFields,
                'repeatableGroups' => $repeatableGroups,
                'editableTokens' => array_values(array_filter($editableTokens, 'is_array')),
                'tokenOverrides' => is_array($section['tokenOverrides'] ?? null) ? $section['tokenOverrides'] : [],
                'customCss' => (string) ($section['customCss'] ?? ''),
                'mediaLayout' => (string) ($section['mediaLayout'] ?? ''),
                'variantRef' => (string) ($section['variantRef'] ?? ''),
                'availableActions' => $isEditable ? ['editContent'] : ['inspect'],
                'section' => $section,
            ];
        }

        return $nodes;
    }

    /**
     * @param array<string, mixed> $section
     */
    private function explicitEditabilityForSection(array $section): ?bool
    {
        $editing = is_array($section['editing'] ?? null) ? $section['editing'] : [];
        if (array_key_exists('contentEditable', $editing)) {
            return filter_var($editing['contentEditable'], FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE) ?? false;
        }

        if (array_key_exists('contentEditable', $section)) {
            return filter_var($section['contentEditable'], FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE) ?? false;
        }

        return null;
    }

    /**
     * @param array<string, mixed> $schema
     */
    private function chromeSummary(array $schema): string
    {
        $chrome = is_array($schema['chrome'] ?? null) ? $schema['chrome'] : [];
        $header = is_array($chrome['header'] ?? null) ? $chrome['header'] : [];
        $footer = is_array($chrome['footer'] ?? null) ? $chrome['footer'] : [];
        $headerVariant = trim((string) ($header['variant'] ?? ''));
        $footerVariant = trim((string) ($footer['variant'] ?? ''));

        return 'Header: ' . ($headerVariant !== '' ? $headerVariant : 'global')
            . ' / Footer: ' . ($footerVariant !== '' ? $footerVariant : 'global');
    }
}
