<?php

declare(strict_types=1);

final class MyronSectionVariantRepository
{
    public function __construct(
        private readonly PDO $pdo,
        private readonly MyronContentRepository $content,
    ) {
    }

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

        return array_values(array_map(fn (array $row): array => $this->hydrate($row), is_array($rows) ? $rows : []));
    }

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

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

    /**
     * @param array<string, mixed> $section
     * @param array<string, mixed> $content
     */
    public function saveFromSection(string $name, array $section, array $content, ?string $userId = null): array
    {
        $design = [
            'customCss' => (string) ($section['customCss'] ?? ''),
            'tokenOverrides' => is_array($section['tokenOverrides'] ?? null) ? $section['tokenOverrides'] : [],
            'editableTokens' => is_array($section['editableTokens'] ?? null) ? $section['editableTokens'] : [],
            'creative' => is_array($section['creative'] ?? null) ? $section['creative'] : null,
        ];

        return $this->createVariant(
            $name,
            trim((string) ($section['sectionKey'] ?? '')),
            (string) ($section['componentId'] ?? ''),
            trim((string) ($section['variant'] ?? 'default')) ?: 'default',
            $design,
            $content,
            $userId
        );
    }

    /**
     * @param array<string, mixed> $design
     * @param array<string, mixed> $content
     */
    public function createVariant(string $name, string $sectionKey, string $componentId, string $variant, array $design, array $content = [], ?string $userId = null): array
    {
        $name = trim($name);
        if ($name === '') {
            throw new InvalidArgumentException('Section variants require a display name.');
        }

        $componentId = trim($componentId);
        if ($componentId === '') {
            throw new InvalidArgumentException('Section variants require a component id.');
        }

        $now = myronNow();
        $id = myronGenerateId('sv');
        $statement = $this->pdo->prepare(
            'INSERT INTO section_variants (
                id, name, section_key, component_id, variant, design_json, content_json, created_by, created_at, updated_at
             ) VALUES (
                :id, :name, :section_key, :component_id, :variant, :design_json, :content_json, :created_by, :created_at, :updated_at
             )'
        );
        $statement->execute([
            ':id' => $id,
            ':name' => $name,
            ':section_key' => trim($sectionKey) ?: null,
            ':component_id' => $componentId,
            ':variant' => trim($variant) ?: 'default',
            ':design_json' => json_encode($design, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES),
            ':content_json' => json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES),
            ':created_by' => $userId,
            ':created_at' => $now,
            ':updated_at' => $now,
        ]);

        $saved = $this->find($id);
        if (!is_array($saved)) {
            throw new RuntimeException('Failed to reload saved section variant.');
        }

        return $saved;
    }

    /**
     * @param array<string, mixed> $design
     * @param array<string, mixed> $content
     */
    public function updateVariant(string $id, string $name, string $sectionKey, string $componentId, string $variant, array $design, array $content = []): array
    {
        $id = trim($id);
        if ($id === '' || !is_array($this->find($id))) {
            throw new InvalidArgumentException('Choose a valid section variant.');
        }
        $name = trim($name);
        $componentId = trim($componentId);
        if ($name === '' || $componentId === '') {
            throw new InvalidArgumentException('Section variants require a name and component id.');
        }

        $statement = $this->pdo->prepare(
            'UPDATE section_variants
             SET name = :name,
                 section_key = :section_key,
                 component_id = :component_id,
                 variant = :variant,
                 design_json = :design_json,
                 content_json = :content_json,
                 updated_at = :updated_at
             WHERE id = :id'
        );
        $statement->execute([
            ':name' => $name,
            ':section_key' => trim($sectionKey) ?: null,
            ':component_id' => $componentId,
            ':variant' => trim($variant) ?: 'default',
            ':design_json' => json_encode($design, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES),
            ':content_json' => json_encode($content, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES),
            ':updated_at' => myronNow(),
            ':id' => $id,
        ]);

        $updated = $this->find($id);
        if (!is_array($updated)) {
            throw new RuntimeException('Failed to reload updated section variant.');
        }

        return $updated;
    }

    public function delete(string $id): void
    {
        $id = trim($id);
        if ($id === '') {
            return;
        }

        $usage = $this->usageCount($id);
        if ($usage > 0) {
            throw new InvalidArgumentException('Section variant is applied to ' . $usage . ' runtime section(s) and cannot be deleted.');
        }

        $statement = $this->pdo->prepare('DELETE FROM section_variants WHERE id = :id');
        $statement->execute([':id' => $id]);
    }

    public function usageCount(string $id): int
    {
        $id = trim($id);
        if ($id === '') {
            return 0;
        }

        $count = 0;
        foreach ($this->content->listTemplates() as $template) {
            $schema = is_array($template['schema'] ?? null) ? $template['schema'] : [];
            $sections = is_array($schema['sections'] ?? null) ? $schema['sections'] : [];
            foreach ($sections as $section) {
                if (is_array($section) && trim((string) ($section['variantRef'] ?? '')) === $id) {
                    $count++;
                }
            }
        }

        return $count;
    }

    /**
     * @param array<string, mixed> $section
     * @return array<string, mixed>
     */
    public function applyToSection(array $section, array $variant): array
    {
        $design = is_array($variant['design'] ?? null) ? $variant['design'] : [];
        $section['variant'] = (string) ($variant['variant'] ?? ($section['variant'] ?? 'default'));
        $section['variantRef'] = (string) ($variant['id'] ?? '');
        $variantCss = trim((string) ($design['customCss'] ?? ''));
        $instanceCss = trim((string) ($section['customCss'] ?? ''));
        $section['customCss'] = $variantCss;
        if ($instanceCss !== '' && $instanceCss !== $variantCss) {
            $section['customCss'] = trim($variantCss . "\n\n/* Per-instance custom CSS */\n" . $instanceCss);
        }

        $variantOverrides = is_array($design['tokenOverrides'] ?? null) ? $design['tokenOverrides'] : [];
        $instanceOverrides = is_array($section['tokenOverrides'] ?? null) ? $section['tokenOverrides'] : [];
        $section['tokenOverrides'] = array_merge($variantOverrides, $instanceOverrides);

        $section['creative'] = is_array($design['creative'] ?? null) ? $design['creative'] : null;

        return $section;
    }

    /**
     * @param array<string, mixed> $row
     * @return array<string, mixed>
     */
    private function hydrate(array $row): array
    {
        $design = json_decode((string) ($row['design_json'] ?? '{}'), true, 512, JSON_THROW_ON_ERROR);
        $content = json_decode((string) ($row['content_json'] ?? '{}'), true, 512, JSON_THROW_ON_ERROR);

        return [
            'id' => (string) ($row['id'] ?? ''),
            'name' => (string) ($row['name'] ?? ''),
            'sectionKey' => (string) ($row['section_key'] ?? ''),
            'componentId' => (string) ($row['component_id'] ?? ''),
            'variant' => (string) ($row['variant'] ?? ''),
            'design' => is_array($design) ? $design : [],
            'content' => is_array($content) ? $content : [],
            'createdBy' => (string) ($row['created_by'] ?? ''),
            'createdAt' => (string) ($row['created_at'] ?? ''),
            'updatedAt' => (string) ($row['updated_at'] ?? ''),
            'usageCount' => $this->usageCount((string) ($row['id'] ?? '')),
        ];
    }
}
