<?php

/**
 * LolaCore - PlaybookFrontmatterNormalizer
 *
 * Repairs common LLM frontmatter mistakes before YamlParser runs.
 *
 * @package LolaCore
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\Playbooks;

/**
 * Keeps upload distillation drafts parseable by our YAML subset parser.
 */
final class PlaybookFrontmatterNormalizer
{
    /** @var array<int,string> */
    private const ALLOWED_KEYS = [
        'name',
        'description',
        'domain',
        'type',
        'version',
        'min_lolacore_version',
        'ability_prefixes',
        'triggers',
        'status',
    ];

    public static function normalize(string $markdown): string
    {
        if (!preg_match('/\A---\r?\n([\s\S]*?)\r?\n---([\s\S]*)\z/', $markdown, $matches) === 1) {
            return $markdown;
        }

        $fields = self::extractFields($matches[1]);
        if ($fields === []) {
            return $markdown;
        }

        $lines = ['---'];

        foreach (self::ALLOWED_KEYS as $key) {
            if (!array_key_exists($key, $fields)) {
                continue;
            }

            $value = $fields[$key];
            if ($key === 'description') {
                $lines[] = 'description: ' . self::quoteDouble((string) $value);
                continue;
            }

            if (is_array($value)) {
                continue;
            }

            $scalar = trim((string) $value);
            if ($scalar === '') {
                continue;
            }

            $lines[] = $key . ': ' . $scalar;
        }

        $lines[] = '---';

        $body = ltrim($matches[2], "\r\n");

        return implode("\n", $lines) . "\n\n" . $body;
    }

    /**
     * @return array<string, mixed>
     */
    private static function extractFields(string $frontmatterBlock): array
    {
        $lines = preg_split("/\r\n|\r|\n/", $frontmatterBlock) ?: [];
        $fields = [];
        $i      = 0;
        $total  = count($lines);

        while ($i < $total) {
            $line = $lines[$i];
            if (trim($line) === '' || str_starts_with(ltrim($line), '#')) {
                ++$i;
                continue;
            }

            if (!preg_match('/^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*(.*)$/u', $line, $match) === 1) {
                ++$i;
                continue;
            }

            $key   = $match[1];
            $value = $match[2];

            if (trim($value) === '') {
                [$folded, $i] = self::readFoldedScalar($lines, $i + 1);
                $fields[$key] = $folded;
                continue;
            }

            $fields[$key] = self::stripScalarQuotes(trim($value));
            ++$i;
        }

        return $fields;
    }

    /**
     * @param array<int,string> $lines
     * @return array{0:string, 1:int}
     */
    private static function readFoldedScalar(array $lines, int $start): array
    {
        $parts = [];
        $i     = $start;
        $total = count($lines);

        while ($i < $total) {
            $line = $lines[$i];
            if (trim($line) === '') {
                ++$i;
                continue;
            }
            if (preg_match('/^[A-Za-z_][A-Za-z0-9_]*\s*:/u', $line) === 1) {
                break;
            }
            $parts[] = trim($line);
            ++$i;
        }

        return [trim(implode(' ', $parts)), $i];
    }

    private static function stripScalarQuotes(string $value): string
    {
        if (
            (str_starts_with($value, '"') && str_ends_with($value, '"'))
            || (str_starts_with($value, "'") && str_ends_with($value, "'"))
        ) {
            return substr($value, 1, -1);
        }

        return $value;
    }

    private static function quoteDouble(string $value): string
    {
        $clean = str_replace(['"', "\r", "\n"], ['', ' ', ' '], trim($value));

        return '"' . $clean . '"';
    }
}
