"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatWorkflowTemplateReferenceUsageTable = formatWorkflowTemplateReferenceUsageTable;
exports.normalizeWorkflowTemplateReferenceTargetPath = normalizeWorkflowTemplateReferenceTargetPath;
exports.collectWorkflowTemplateReferenceUsages = collectWorkflowTemplateReferenceUsages;
exports.collectWorkflowTemplateReferenceUsagesFromCatalog = collectWorkflowTemplateReferenceUsagesFromCatalog;
const node_path_1 = __importDefault(require("node:path"));
const types_1 = require("./types");
const validation_1 = require("./validation");
function formatWorkflowTemplateReferenceUsageTable(usages) {
    return [
        "targetPath\tusageKind\tsourceType\tsourcePath\tlineNumber\teditable\tline",
        ...usages.map((usage) => [
            usage.targetPath,
            usage.usageKind,
            usage.sourceType,
            usage.sourcePath,
            usage.lineNumber ?? "",
            usage.editable ? "yes" : "no",
            usage.sourceLine,
        ].join("\t")),
    ].join("\n");
}
function normalizeWorkflowTemplateReferenceTargetPath(value) {
    const repoPrefix = `${types_1.WORKFLOW_TEMPLATE_ROOT_DIRECTORY_NAME}/${types_1.WORKFLOW_TEMPLATE_DIRECTORY_NAME}/`;
    const trimmed = value.trim().replace(/^\.\//u, "");
    return (0, types_1.normalizeWorkflowTemplatePath)(trimmed.startsWith(repoPrefix) ? trimmed.slice(repoPrefix.length) : trimmed);
}
function collectWorkflowTemplateReferenceUsages(input) {
    const targetPath = normalizeWorkflowTemplateReferenceTargetPath(input.targetPath);
    if (targetPath === null) {
        return [];
    }
    const usages = [
        ...collectWorkflowTemplateReferenceUsagesFromCatalog({
            catalog: input.catalog,
            targetPath,
            includeSystemSources: input.includeSystemSources,
        }),
        ...collectWorkflowTemplateReferenceUsagesFromConfig({
            config: input.config ?? null,
            targetPath,
        }),
    ];
    return sortWorkflowTemplateReferenceUsages(dedupeReferenceUsages(usages));
}
function collectWorkflowTemplateReferenceUsagesFromCatalog(input) {
    const targetPath = normalizeWorkflowTemplateReferenceTargetPath(input.targetPath);
    if (targetPath === null) {
        return [];
    }
    return input.catalog.templates.flatMap((template) => {
        if (!input.includeSystemSources && template.system) {
            return [];
        }
        return collectWorkflowTemplateReferenceUsagesFromTemplate({
            template,
            targetPath,
        });
    });
}
function collectWorkflowTemplateReferenceUsagesFromTemplate(input) {
    const usages = [];
    const workflowRefLines = new Set();
    for (const directive of (0, validation_1.scanWorkflowTemplateDirectives)(input.template)
        .directives) {
        if (directive.templateName !== input.targetPath) {
            continue;
        }
        workflowRefLines.add(directive.lineNumber);
        usages.push({
            targetPath: input.targetPath,
            usageKind: "workflow_ref",
            sourceType: "template",
            sourcePath: input.template.templatePath,
            sourceTemplatePath: input.template.templatePath,
            editable: isEditableWorkflowTemplateUsage(input.template),
            lineNumber: directive.sourceLineNumber ?? directive.lineNumber,
            sourceLine: directive.sourceLine,
        });
    }
    const lines = input.template.body.split("\n");
    let inFence = false;
    for (const [index, line] of lines.entries()) {
        const lineNumber = index + 1;
        const trimmed = line.trimStart();
        if (/^(```|~~~)/u.test(trimmed)) {
            inFence = !inFence;
            continue;
        }
        if (inFence || workflowRefLines.has(lineNumber)) {
            continue;
        }
        const sourceLineNumber = input.template.bodyStartLineNumber !== undefined &&
            input.template.bodyStartLineNumber > 1
            ? input.template.bodyStartLineNumber + index
            : lineNumber;
        usages.push(...scanTemplateLineForReferenceUsages({
            line,
            lineNumber: sourceLineNumber,
            template: input.template,
            targetPath: input.targetPath,
        }));
    }
    return usages;
}
function scanTemplateLineForReferenceUsages(input) {
    const usages = [];
    const occupiedRanges = [];
    const referenceDefinitionMatch = input.line.match(/^\s{0,3}\[[^\]]+\]:\s*(<[^>\s]+>|[^\s]+)/u);
    if (referenceDefinitionMatch?.[1] !== undefined) {
        const rawTarget = unwrapMarkdownAutolinkTarget(referenceDefinitionMatch[1]);
        const start = referenceDefinitionMatch.index ?? 0;
        const end = start + referenceDefinitionMatch[0].length;
        occupiedRanges.push({ start, end });
        const normalizedTarget = normalizeReferencedWorkflowTemplatePath({
            rawTarget,
            sourceTemplatePath: input.template.templatePath,
            preferRelative: true,
        });
        if (normalizedTarget === input.targetPath) {
            usages.push(buildTemplateReferenceUsage({
                template: input.template,
                targetPath: input.targetPath,
                usageKind: "markdown_link",
                lineNumber: input.lineNumber,
                sourceLine: input.line.trim(),
            }));
        }
    }
    for (const match of input.line.matchAll(/\[[^\]]*\]\(([^)\s]+)(?:\s+[^)]*)?\)/gu)) {
        const rawTarget = match[1];
        const start = match.index ?? 0;
        const end = start + match[0].length;
        occupiedRanges.push({ start, end });
        const normalizedTarget = normalizeReferencedWorkflowTemplatePath({
            rawTarget,
            sourceTemplatePath: input.template.templatePath,
            preferRelative: true,
        });
        if (normalizedTarget === input.targetPath) {
            usages.push(buildTemplateReferenceUsage({
                template: input.template,
                targetPath: input.targetPath,
                usageKind: "markdown_link",
                lineNumber: input.lineNumber,
                sourceLine: input.line.trim(),
            }));
        }
    }
    for (const match of input.line.matchAll(/<([^>\s]+)>/gu)) {
        const rawTarget = match[1];
        const start = match.index ?? 0;
        const end = start + match[0].length;
        occupiedRanges.push({ start, end });
        const normalizedTarget = normalizeReferencedWorkflowTemplatePath({
            rawTarget,
            sourceTemplatePath: input.template.templatePath,
            preferRelative: true,
        });
        if (normalizedTarget === input.targetPath) {
            usages.push(buildTemplateReferenceUsage({
                template: input.template,
                targetPath: input.targetPath,
                usageKind: "markdown_link",
                lineNumber: input.lineNumber,
                sourceLine: input.line.trim(),
            }));
        }
    }
    const plainPathPattern = /(?:^|[\s`"'([{<])((?:\.\/)?(?:\.alpaca\/workflows\/)?[^\s`"'()[\]{}<>]+\.md(?:[?#][^\s`"'()[\]{}<>]*)?)(?=$|[\s`"',.;:!?)[\]}>])/gu;
    for (const match of input.line.matchAll(plainPathPattern)) {
        const rawTarget = match[1];
        if (rawTarget === undefined) {
            continue;
        }
        const rawStart = match.index ?? 0;
        const start = rawStart + match[0].indexOf(rawTarget);
        const end = start + rawTarget.length;
        if (occupiedRanges.some((range) => start >= range.start && end <= range.end)) {
            continue;
        }
        const normalizedTarget = normalizeReferencedWorkflowTemplatePath({
            rawTarget,
            sourceTemplatePath: input.template.templatePath,
            preferRelative: false,
        });
        if (normalizedTarget === input.targetPath) {
            usages.push(buildTemplateReferenceUsage({
                template: input.template,
                targetPath: input.targetPath,
                usageKind: "document_path",
                lineNumber: input.lineNumber,
                sourceLine: input.line.trim(),
            }));
        }
    }
    return usages;
}
function unwrapMarkdownAutolinkTarget(rawTarget) {
    return rawTarget.startsWith("<") && rawTarget.endsWith(">")
        ? rawTarget.slice(1, -1)
        : rawTarget;
}
function buildTemplateReferenceUsage(input) {
    return {
        targetPath: input.targetPath,
        usageKind: input.usageKind,
        sourceType: "template",
        sourcePath: input.template.templatePath,
        sourceTemplatePath: input.template.templatePath,
        editable: isEditableWorkflowTemplateUsage(input.template),
        lineNumber: input.lineNumber,
        sourceLine: input.sourceLine,
    };
}
function normalizeReferencedWorkflowTemplatePath(input) {
    if (input.rawTarget === undefined) {
        return null;
    }
    const withoutAnchor = input.rawTarget.split("#")[0]?.split("?")[0] ?? "";
    if (/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(withoutAnchor)) {
        return null;
    }
    const repoPrefix = `${types_1.WORKFLOW_TEMPLATE_ROOT_DIRECTORY_NAME}/${types_1.WORKFLOW_TEMPLATE_DIRECTORY_NAME}/`;
    const normalizedRaw = withoutAnchor.replace(/^\.\//u, "");
    if (normalizedRaw.startsWith(repoPrefix)) {
        return (0, types_1.normalizeWorkflowTemplatePath)(normalizedRaw.slice(repoPrefix.length));
    }
    const explicitRelative = withoutAnchor.startsWith("./") || withoutAnchor.startsWith("../");
    if (input.preferRelative || explicitRelative) {
        const relative = normalizeRelativeWorkflowTemplatePath({
            sourceTemplatePath: input.sourceTemplatePath,
            rawTarget: withoutAnchor,
        });
        if (relative !== null) {
            return relative;
        }
    }
    const direct = (0, types_1.normalizeWorkflowTemplatePath)(normalizedRaw);
    if (direct !== null) {
        return direct;
    }
    if (!input.preferRelative && !explicitRelative) {
        return normalizeRelativeWorkflowTemplatePath({
            sourceTemplatePath: input.sourceTemplatePath,
            rawTarget: normalizedRaw,
        });
    }
    return null;
}
function normalizeRelativeWorkflowTemplatePath(input) {
    const sourceDirectory = node_path_1.default.posix.dirname(input.sourceTemplatePath);
    if (sourceDirectory === ".") {
        return null;
    }
    return (0, types_1.normalizeWorkflowTemplatePath)(node_path_1.default.posix.normalize(node_path_1.default.posix.join(sourceDirectory, input.rawTarget)));
}
function collectWorkflowTemplateReferenceUsagesFromConfig(input) {
    if (input.config === null) {
        return [];
    }
    const usages = [];
    for (const [surface, selector] of Object.entries(input.config.workflowTemplateDefaults)) {
        if (selector.mode === "template" &&
            selector.templatePath === input.targetPath) {
            usages.push({
                targetPath: input.targetPath,
                usageKind: "config_selector",
                sourceType: "config",
                sourcePath: "alpaca.config.json",
                editable: false,
                lineNumber: null,
                sourceLine: `workflowTemplateDefaults.${surface}`,
            });
        }
    }
    for (const [index, templatePath,] of input.config.followup.preferredWorkflowTemplates.entries()) {
        if (templatePath === input.targetPath) {
            usages.push({
                targetPath: input.targetPath,
                usageKind: "config_selector",
                sourceType: "config",
                sourcePath: "alpaca.config.json",
                editable: false,
                lineNumber: null,
                sourceLine: `followup.preferredWorkflowTemplates[${index}]`,
            });
        }
    }
    return usages;
}
function isEditableWorkflowTemplateUsage(template) {
    return (!template.system &&
        template.source === "repository" &&
        (template.updateMode ?? "flexible") === "flexible");
}
function dedupeReferenceUsages(usages) {
    const seen = new Set();
    const deduped = [];
    for (const usage of usages) {
        const key = [
            usage.sourceType,
            usage.sourcePath,
            usage.lineNumber ?? "",
            usage.usageKind,
            usage.sourceLine,
        ].join("\0");
        if (seen.has(key)) {
            continue;
        }
        seen.add(key);
        deduped.push(usage);
    }
    return deduped;
}
function sortWorkflowTemplateReferenceUsages(usages) {
    return [...usages].sort((left, right) => {
        const pathOrder = left.sourcePath.localeCompare(right.sourcePath);
        if (pathOrder !== 0) {
            return pathOrder;
        }
        return (left.lineNumber ?? 0) - (right.lineNumber ?? 0);
    });
}
