<?php

declare(strict_types=1);

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

    /**
     * @return list<array<string, mixed>>
     */
    public function listOverrides(): array
    {
        $statement = $this->pdo->query('SELECT * FROM section_bridge_overrides ORDER BY section_key ASC');
        $rows = $statement ? $statement->fetchAll() : [];

        if (!is_array($rows)) {
            return [];
        }

        return array_map(fn (array $row): array => $this->hydrateOverride($row), $rows);
    }

    public function findOverrideBySectionKey(string $sectionKey): ?array
    {
        $statement = $this->pdo->prepare('SELECT * FROM section_bridge_overrides WHERE section_key = :section_key LIMIT 1');
        $statement->execute([':section_key' => trim($sectionKey)]);
        $row = $statement->fetch();

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

    /**
     * @param list<string> $primaryComponents
     * @param 0|1|null $defaultContentEditable
     * @return array<string, mixed>
     */
    public function saveOverride(
        string $sectionKey,
        ?string $label,
        ?string $description,
        array $primaryComponents,
        string $remediationStatus,
        ?int $defaultContentEditable,
        string $actorId = ''
    ): array {
        $normalizedSectionKey = trim($sectionKey);
        if ($normalizedSectionKey === '') {
            throw new InvalidArgumentException('Section bridge overrides require a governed section key.');
        }

        $normalizedComponents = self::normalizePrimaryComponents($primaryComponents);
        $status = self::normalizeStatus($remediationStatus);
        $now = myronNow();
        $componentsJson = json_encode($normalizedComponents, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
        $payload = [
            'sectionKey' => $normalizedSectionKey,
            'label' => $label !== null ? trim($label) : null,
            'description' => $description !== null ? trim($description) : null,
            'primaryComponents' => $normalizedComponents,
            'remediationStatus' => $status,
            'defaultContentEditable' => $defaultContentEditable,
            'updatedByUserId' => trim($actorId),
        ];
        $payloadJson = json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);

        if ($this->findOverrideBySectionKey($normalizedSectionKey) !== null) {
            $statement = $this->pdo->prepare(
                'UPDATE section_bridge_overrides
                 SET label_override = :label_override,
                     description_override = :description_override,
                     primary_components_json = :primary_components_json,
                     remediation_status = :remediation_status,
                     default_content_editable = :default_content_editable,
                     record_json = :record_json,
                     updated_by_user_id = :updated_by_user_id,
                     updated_at = :updated_at
                 WHERE section_key = :section_key'
            );
            $statement->execute([
                ':label_override' => $payload['label'],
                ':description_override' => $payload['description'],
                ':primary_components_json' => $componentsJson,
                ':remediation_status' => $status,
                ':default_content_editable' => $defaultContentEditable,
                ':record_json' => $payloadJson,
                ':updated_by_user_id' => trim($actorId),
                ':updated_at' => $now,
                ':section_key' => $normalizedSectionKey,
            ]);
        } else {
            $statement = $this->pdo->prepare(
                'INSERT INTO section_bridge_overrides (
                    section_key,
                    label_override,
                    description_override,
                    primary_components_json,
                    remediation_status,
                    default_content_editable,
                    record_json,
                    updated_by_user_id,
                    created_at,
                    updated_at
                 ) VALUES (
                    :section_key,
                    :label_override,
                    :description_override,
                    :primary_components_json,
                    :remediation_status,
                    :default_content_editable,
                    :record_json,
                    :updated_by_user_id,
                    :created_at,
                    :updated_at
                 )'
            );
            $statement->execute([
                ':section_key' => $normalizedSectionKey,
                ':label_override' => $payload['label'],
                ':description_override' => $payload['description'],
                ':primary_components_json' => $componentsJson,
                ':remediation_status' => $status,
                ':default_content_editable' => $defaultContentEditable,
                ':record_json' => $payloadJson,
                ':updated_by_user_id' => trim($actorId),
                ':created_at' => $now,
                ':updated_at' => $now,
            ]);
        }

        $saved = $this->findOverrideBySectionKey($normalizedSectionKey);
        if (!is_array($saved)) {
            throw new RuntimeException('Failed to reload section bridge override for `' . $normalizedSectionKey . '`.');
        }

        return $saved;
    }

    /**
     * @param array<string, mixed> $section
     * @return array<string, mixed>
     */
    public function mergeSection(array $section): array
    {
        $sectionKey = trim((string) ($section['sectionKey'] ?? ''));
        if ($sectionKey === '') {
            return $section;
        }

        $override = $this->findOverrideBySectionKey($sectionKey);
        if (!is_array($override)) {
            $section['defaultContentEditable'] = $section['defaultContentEditable'] ?? null;
            $section['_bridge'] = [
                'hasOverride' => false,
                'sourceAuthority' => 'canonical-section-registry',
            ];
            return $section;
        }

        $label = trim((string) ($override['label'] ?? ''));
        $description = trim((string) ($override['description'] ?? ''));
        $components = is_array($override['primaryComponents'] ?? null) ? $override['primaryComponents'] : [];
        $status = trim((string) ($override['remediationStatus'] ?? ''));

        if ($label !== '') {
            $section['label'] = $label;
        }
        if ($description !== '') {
            $section['description'] = $description;
        }
        if ($components !== []) {
            $section['primaryComponents'] = array_values(array_map('strval', $components));
        }
        if ($status !== '') {
            $section['remediationStatus'] = $status;
        }

        $section['defaultContentEditable'] = $override['defaultContentEditable'];
        $section['_bridge'] = [
            'hasOverride' => true,
            'sourceAuthority' => 'runtime-section-bridge',
            'updatedByUserId' => (string) ($override['updatedByUserId'] ?? ''),
            'updatedAt' => (string) ($override['updatedAt'] ?? ''),
        ];

        return $section;
    }

    /**
     * @param list<array<string, mixed>> $sections
     * @return list<array<string, mixed>>
     */
    public function mergeSections(array $sections): array
    {
        return array_map(fn (array $section): array => $this->mergeSection($section), $sections);
    }

    /**
     * @param list<string> $primaryComponents
     * @return list<string>
     */
    public static function normalizePrimaryComponents(array $primaryComponents): array
    {
        $normalized = [];
        foreach ($primaryComponents as $componentId) {
            $componentId = trim((string) $componentId);
            if ($componentId !== '') {
                $normalized[$componentId] = true;
            }
        }

        $components = array_keys($normalized);
        if ($components === []) {
            throw new InvalidArgumentException('Section bridge metadata requires at least one primary render component.');
        }

        return $components;
    }

    public static function normalizeStatus(string $status): string
    {
        $normalized = trim($status);
        if ($normalized === '') {
            return 'governed';
        }

        return $normalized;
    }

    /**
     * @param array<string, mixed> $row
     * @return array<string, mixed>
     */
    private function hydrateOverride(array $row): array
    {
        $components = json_decode((string) ($row['primary_components_json'] ?? '[]'), true, 512, JSON_THROW_ON_ERROR);
        if (!is_array($components)) {
            $components = [];
        }

        $editable = $row['default_content_editable'] ?? null;
        $defaultContentEditable = $editable === null || $editable === ''
            ? null
            : ((int) $editable === 1 ? 1 : 0);

        return [
            'sectionKey' => (string) ($row['section_key'] ?? ''),
            'label' => (string) ($row['label_override'] ?? ''),
            'description' => (string) ($row['description_override'] ?? ''),
            'primaryComponents' => array_values(array_map('strval', $components)),
            'remediationStatus' => (string) ($row['remediation_status'] ?? ''),
            'defaultContentEditable' => $defaultContentEditable,
            'updatedByUserId' => (string) ($row['updated_by_user_id'] ?? ''),
            'createdAt' => (string) ($row['created_at'] ?? ''),
            'updatedAt' => (string) ($row['updated_at'] ?? ''),
        ];
    }
}
