<?php

declare(strict_types=1);

final class MyronCustomSectionTemplateActionService
{
    public function __construct(
        private readonly PDO $pdo,
        private readonly MyronCustomSectionTemplateRepository $templates,
        private readonly MyronComponentRegistryRepository $components,
        private readonly MyronPrimitiveRenderer $renderer,
    ) {
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function create(array $input, MyronActionCallerContext $caller): array
    {
        $sectionKey = MyronCustomSectionTemplateRepository::normalizeSectionKey((string) ($input['sectionKey'] ?? $input['key'] ?? $input['label'] ?? ''));
        if ($sectionKey === '') {
            throw new InvalidArgumentException('sectionKey or label is required.');
        }
        if ($this->customExists($sectionKey)) {
            throw new InvalidArgumentException('A custom section template already exists for `' . $sectionKey . '`.');
        }
        $this->assertKeyIsAvailable($sectionKey);

        return $this->templates->save($this->normalizeRecord($input, $sectionKey), $this->actorId($caller));
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    public function update(string $sectionKey, array $input, MyronActionCallerContext $caller): array
    {
        $normalized = MyronCustomSectionTemplateRepository::normalizeSectionKey($sectionKey);
        $existing = $this->templates->findBySectionKey($normalized, true);
        if (!is_array($existing)) {
            throw new InvalidArgumentException('Custom section template not found: ' . $normalized);
        }

        $record = array_merge($existing, $input);
        $record['sectionKey'] = $normalized;
        return $this->templates->save($this->normalizeRecord($record, $normalized), $this->actorId($caller));
    }

    /**
     * @return array<string, mixed>
     */
    public function get(string $sectionKey): array
    {
        $template = $this->templates->findBySectionKey($sectionKey, true);
        if (!is_array($template)) {
            throw new InvalidArgumentException('Custom section template not found: ' . $sectionKey);
        }

        $template['usages'] = $this->templates->usagesForSectionKey($sectionKey);
        return $template;
    }

    /**
     * @return array{sectionKey:string,deleted:bool}
     */
    public function delete(string $sectionKey): array
    {
        $normalized = MyronCustomSectionTemplateRepository::normalizeSectionKey($sectionKey);
        if ($normalized === '') {
            throw new InvalidArgumentException('sectionKey is required.');
        }
        if (!is_array($this->templates->findBySectionKey($normalized, true))) {
            throw new InvalidArgumentException('Custom section template not found: ' . $normalized);
        }
        $usages = $this->templates->usagesForSectionKey($normalized);
        if ($usages !== []) {
            throw new RuntimeException('Custom section template `' . $normalized . '` is in use and cannot be deleted.');
        }

        $this->templates->delete($normalized);
        return ['sectionKey' => $normalized, 'deleted' => true];
    }

    /**
     * @param array<string, mixed> $input
     * @return array<string, mixed>
     */
    private function normalizeRecord(array $input, string $sectionKey): array
    {
        if (in_array($sectionKey, MyronSectionRegistryRepository::forbiddenBodySectionKeys(), true)) {
            throw new InvalidArgumentException('`' . $sectionKey . '` is reserved for primitive/chrome use and cannot be a custom body section.');
        }

        $componentId = trim((string) ($input['primaryComponentId'] ?? $input['componentId'] ?? ''));
        $this->assertRenderableComponent($componentId);

        $editing = is_array($input['editing'] ?? null) ? (array) $input['editing'] : [
            'fields' => is_array($input['fields'] ?? null) ? (array) $input['fields'] : [],
            'repeaters' => is_array($input['repeaters'] ?? null) ? (array) $input['repeaters'] : [],
        ];

        return [
            'sectionKey' => $sectionKey,
            'label' => trim((string) ($input['label'] ?? ucwords(str_replace('-', ' ', $sectionKey)))),
            'description' => trim((string) ($input['description'] ?? '')),
            'jtbdGroup' => trim((string) ($input['jtbdGroup'] ?? 'custom')),
            'sectionType' => trim((string) ($input['sectionType'] ?? 'custom')),
            'primaryComponentId' => $componentId,
            'variant' => MyronCustomSectionTemplateRepository::normalizeVariant((string) ($input['variant'] ?? 'default')),
            'defaultContent' => is_array($input['defaultContent'] ?? null) ? (array) $input['defaultContent'] : [],
            'defaultContentEditable' => $input['defaultContentEditable'] ?? true,
            'editing' => MyronCustomSectionTemplateRepository::normalizeEditingContract($editing),
            'lifecycleStatus' => (string) ($input['lifecycleStatus'] ?? 'active'),
        ];
    }

    private function assertKeyIsAvailable(string $sectionKey): void
    {
        $statement = $this->pdo->prepare('SELECT 1 FROM section_registry WHERE section_key = :section_key LIMIT 1');
        $statement->execute([':section_key' => $sectionKey]);
        if ($statement->fetchColumn() !== false) {
            throw new InvalidArgumentException('`' . $sectionKey . '` is a canonical section key and cannot be reused for a custom section.');
        }
    }

    private function customExists(string $sectionKey): bool
    {
        return is_array($this->templates->findBySectionKey($sectionKey, true));
    }

    private function assertRenderableComponent(string $componentId): void
    {
        if ($componentId === '') {
            throw new InvalidArgumentException('primaryComponentId is required.');
        }
        $component = $this->components->findComponentById($componentId);
        if (!is_array($component)) {
            throw new InvalidArgumentException('Primary component does not exist: ' . $componentId);
        }
        if (!$this->renderer->canRender($componentId)) {
            throw new InvalidArgumentException('Primary component is not renderable as a section: ' . $componentId);
        }
    }

    private function actorId(MyronActionCallerContext $caller): string
    {
        return $caller->callerType === 'human-session' ? trim($caller->callerId) : '';
    }
}
