<?php

declare(strict_types=1);

final class MyronSectionContentDefaultsRepository
{
    public function __construct(
        private readonly PDO $pdo,
    ) {
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findDefault(string $sectionKey, string $componentId = '', string $variant = ''): ?array
    {
        $normalizedSectionKey = self::normalizeSectionKey($sectionKey);
        if ($normalizedSectionKey === '') {
            return null;
        }

        $statement = $this->pdo->prepare(
            'SELECT *
             FROM section_content_defaults
             WHERE section_key = :section_key
             LIMIT 1'
        );
        $statement->execute([
            ':section_key' => $normalizedSectionKey,
        ]);
        $row = $statement->fetch();

        return is_array($row) ? $this->hydrateDefault($row) : null;
    }

    /**
     * @param array<string, mixed> $content
     * @param array<string, mixed> $flags
     * @param 0|1|null $defaultContentEditable
     * @return array<string, mixed>
     */
    public function saveDefault(
        string $sectionKey,
        string $componentId,
        string $variant,
        array $content,
        ?int $defaultContentEditable,
        array $flags = [],
        string $actorId = ''
    ): array {
        $normalizedSectionKey = self::normalizeSectionKey($sectionKey);
        $normalizedComponentId = MyronSectionComponentDefaultsRepository::normalizeComponentId($componentId);
        $normalizedVariant = MyronSectionComponentDefaultsRepository::normalizeVariant($variant);
        if ($normalizedSectionKey === '') {
            throw new InvalidArgumentException('Section defaults require a governed section key.');
        }
        if ($normalizedComponentId === '') {
            throw new InvalidArgumentException('Section defaults require a render component ID.');
        }

        $now = myronNow();
        $contentJson = json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
        $flagsJson = json_encode($flags, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
        $existing = $this->findDefault($normalizedSectionKey);

        if (is_array($existing)) {
            $statement = $this->pdo->prepare(
                'UPDATE section_content_defaults
                 SET component_id = :component_id,
                     variant = :variant,
                     default_content_json = :default_content_json,
                     default_content_editable = :default_content_editable,
                     flags_json = :flags_json,
                     updated_by_user_id = :updated_by_user_id,
                     updated_at = :updated_at
                 WHERE section_key = :section_key'
            );
            $statement->execute([
                ':component_id' => $normalizedComponentId,
                ':variant' => $normalizedVariant,
                ':default_content_json' => $contentJson,
                ':default_content_editable' => $defaultContentEditable,
                ':flags_json' => $flagsJson,
                ':updated_by_user_id' => trim($actorId),
                ':updated_at' => $now,
                ':section_key' => $normalizedSectionKey,
            ]);
        } else {
            $statement = $this->pdo->prepare(
                'INSERT INTO section_content_defaults (
                    id,
                    section_key,
                    component_id,
                    variant,
                    default_content_json,
                    default_content_editable,
                    flags_json,
                    updated_by_user_id,
                    created_at,
                    updated_at
                 ) VALUES (
                    :id,
                    :section_key,
                    :component_id,
                    :variant,
                    :default_content_json,
                    :default_content_editable,
                    :flags_json,
                    :updated_by_user_id,
                    :created_at,
                    :updated_at
                 )'
            );
            $statement->execute([
                ':id' => myronGenerateId('secctdef'),
                ':section_key' => $normalizedSectionKey,
                ':component_id' => $normalizedComponentId,
                ':variant' => $normalizedVariant,
                ':default_content_json' => $contentJson,
                ':default_content_editable' => $defaultContentEditable,
                ':flags_json' => $flagsJson,
                ':updated_by_user_id' => trim($actorId),
                ':created_at' => $now,
                ':updated_at' => $now,
            ]);
        }

        $saved = $this->findDefault($normalizedSectionKey);
        if (!is_array($saved)) {
            throw new RuntimeException('Failed to reload section content defaults.');
        }

        return $saved;
    }

    public static function normalizeSectionKey(string $sectionKey): string
    {
        return trim($sectionKey);
    }

    /**
     * @param array<string, mixed> $row
     * @return array<string, mixed>
     */
    private function hydrateDefault(array $row): array
    {
        $content = json_decode((string) ($row['default_content_json'] ?? '{}'), true, 512, JSON_THROW_ON_ERROR);
        $flags = json_decode((string) ($row['flags_json'] ?? '{}'), true, 512, JSON_THROW_ON_ERROR);
        $editable = $row['default_content_editable'] ?? null;

        return [
            'id' => (string) ($row['id'] ?? ''),
            'sectionKey' => (string) ($row['section_key'] ?? ''),
            'componentId' => (string) ($row['component_id'] ?? ''),
            'variant' => (string) ($row['variant'] ?? ''),
            'identityKey' => (string) ($row['section_key'] ?? ''),
            'defaultContent' => is_array($content) ? $content : [],
            'defaultContentEditable' => $editable === null || $editable === '' ? null : ((int) $editable === 1 ? 1 : 0),
            'flags' => is_array($flags) ? $flags : [],
            'updatedByUserId' => (string) ($row['updated_by_user_id'] ?? ''),
            'createdAt' => (string) ($row['created_at'] ?? ''),
            'updatedAt' => (string) ($row['updated_at'] ?? ''),
        ];
    }
}
