// Copyright (c) 2026 Modelware. All rights reserved.

import { parse } from 'yaml';
import type { TemplateBindingSource, TemplateValue } from './types.js';

export interface TemplateComposeDirective {
    id: string;
    args: Record<string, TemplateValue>;
    bind: Record<string, TemplateBindingSource>;
}

export function parseTemplateComposeDirective(source: string): TemplateComposeDirective | undefined {
    const normalized = source.replace(/^\uFEFF/, '');
    const frontMatterMatch = /^(---[ \t]*\r?\n[\s\S]*?\r?\n---[ \t]*)([\s\S]*)$/.exec(normalized);
    if (!frontMatterMatch) {
        return undefined;
    }
    const trailing = frontMatterMatch[2] ?? '';
    if (trailing.trim().length > 0) {
        return undefined;
    }
    const yamlBody = frontMatterMatch[1]
        .replace(/^---[ \t]*\r?\n/, '')
        .replace(/\r?\n---[ \t]*$/, '');
    const parsed = parse(yamlBody);
    if (!isRecord(parsed)) {
        return undefined;
    }
    const id = normalizeString(parsed.id);
    if (!id) {
        return undefined;
    }
    const args = parseArgs(parsed.args);
    const bind = parseBind(parsed.bind);
    return { id, args, bind };
}

function parseArgs(value: unknown): Record<string, TemplateValue> {
    if (!isRecord(value)) {
        return {};
    }
    const args: Record<string, TemplateValue> = {};
    for (const [key, raw] of Object.entries(value)) {
        const normalizedKey = normalizeString(key);
        if (!normalizedKey) {
            continue;
        }
        const converted = toTemplateValue(raw);
        if (converted !== undefined) {
            args[normalizedKey] = converted;
        }
    }
    return args;
}

function parseBind(value: unknown): Record<string, TemplateBindingSource> {
    if (!isRecord(value)) {
        return {};
    }
    const bind: Record<string, TemplateBindingSource> = {};
    for (const [key, raw] of Object.entries(value)) {
        const normalizedKey = normalizeString(key);
        const source = normalizeString(raw);
        if (!normalizedKey || !source || !isTemplateBindingSource(source)) {
            continue;
        }
        bind[normalizedKey] = source;
    }
    return bind;
}

function toTemplateValue(value: unknown): TemplateValue | undefined {
    if (value === null) {
        return null;
    }
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        return value;
    }
    if (Array.isArray(value)) {
        return value.every((entry) => typeof entry === 'string') ? value : undefined;
    }
    if (isRecord(value)) {
        return value;
    }
    return undefined;
}

function isTemplateBindingSource(value: string): value is TemplateBindingSource {
    return value === 'context.member'
        || value === 'context.ontology'
        || value === 'context.modelUri'
        || value === 'context.selection[*]'
        || value === 'user'
        || /^context\.selection\[\d+]$/.test(value);
}

function normalizeString(value: unknown): string | undefined {
    if (typeof value !== 'string') {
        return undefined;
    }
    const normalized = value.trim();
    return normalized.length > 0 ? normalized : undefined;
}

function isRecord(value: unknown): value is Record<string, unknown> {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
