import { extractResourceFromPath } from "../../utils/routeParsers.js";
import { logger } from "../../utils/logger.js";
/** Resolve the primary step and inferred test type for a scenario. */
function resolvePrimaryStep(scenario) {
    const testType = scenario.testType ?? (scenario.steps.length === 1 ? "contract" : "integration");
    const mutatingSteps = scenario.steps.filter(st => ["POST", "PUT", "PATCH", "DELETE"].includes(st.method));
    // Use the last mutating step — earlier steps are typically prerequisite setup
    // (e.g. POST /products before PATCH /orders), while the final mutation is the
    // primary action under test.
    const primaryStep = mutatingSteps[mutatingSteps.length - 1] ?? scenario.steps[scenario.steps.length - 1];
    return { primaryStep, testType };
}
export function scenarioCoverageKey(scenario) {
    const { primaryStep, testType } = resolvePrimaryStep(scenario);
    const resource = extractResourceFromPath(primaryStep?.path ?? "");
    return `${resource}::${testType}`;
}
/**
 * Method-aware coverage key for external test dedup.
 * Unlike scenarioCoverageKey (resource::testType), this includes the HTTP method
 * so that e.g. an external test covering "GET /orders" doesn't block generating
 * a test for "PUT /orders" — a different operation on the same resource.
 */
export function externalDedupKey(scenario) {
    const { primaryStep, testType } = resolvePrimaryStep(scenario);
    const method = primaryStep?.method ?? "GET";
    const resource = extractResourceFromPath(primaryStep?.path ?? "");
    return `${method}::${resource}::${testType}`;
}
export function isAttackSurfaceSecurityBoundary(scenario) {
    return scenario.category === "security_boundary" &&
        (scenario.isAttackSurfaceSecurityBoundary === true ||
            scenario.description.startsWith("Attack-surface auth boundary:"));
}
export function isOrdinaryDirectAuthBoundary(scenario) {
    return scenario.category === "security_boundary" &&
        !isAttackSurfaceSecurityBoundary(scenario) &&
        scenario.description.startsWith("Auth boundary:");
}
/**
 * Build a set of coverage keys from external (non-Skyramp) tests.
 * Parses `testLocations` entries tagged with `[external]` to extract the
 * method-aware `METHOD::resource::testType` keys they cover.
 */
export function buildExternalCoverageSet(testLocations) {
    const coverage = new Set();
    let externalWithoutCoverage = 0;
    for (const [testType, fileList] of Object.entries(testLocations)) {
        const externalCount = (fileList.match(/\[external\]/g) || []).length;
        const coveredCount = (fileList.match(/\[external\]\s*\(covers:/g) || []).length;
        externalWithoutCoverage += externalCount - coveredCount;
        for (const m of fileList.matchAll(/\[external\]\s*\(covers:\s*([^)]+)\)/g)) {
            const endpoints = m[1].split(",").map(e => e.trim());
            for (const ep of endpoints) {
                const spaceIdx = ep.indexOf(" ");
                if (spaceIdx < 0)
                    continue;
                const method = ep.slice(0, spaceIdx).toUpperCase();
                const epPath = ep.slice(spaceIdx + 1);
                const resource = extractResourceFromPath(epPath);
                if (resource !== "unknown") {
                    if (testType === "unknown") {
                        coverage.add(`${method}::${resource}::integration`);
                        coverage.add(`${method}::${resource}::contract`);
                    }
                    else {
                        coverage.add(`${method}::${resource}::${testType}`);
                    }
                }
            }
        }
    }
    if (externalWithoutCoverage > 0) {
        logger.info(`${externalWithoutCoverage} external test file(s) have no extractable endpoint coverage — ` +
            `programmatic dedup skipped for these; prompt-level semantic coverage check is the fallback.`);
    }
    return coverage;
}
// Shared TestBot task and step labels used by prompt modules that cannot import
// testbot-prompts.ts directly without creating a circular dependency.
export const TASK_UI_PRESCAN = "0";
export const TASK_ANALYZE_MAINTAIN = "1";
export const TASK_GENERATE = "2";
export const TASK_SUBMIT = "3";
export const TESTBOT_TASK1_STEP_ANALYZE = "1";
export const TESTBOT_TASK1_STEP_MAINTAIN = "2";
export const TESTBOT_TASK1_STEP_CODE_REVIEW = "3";
export const TESTBOT_TASK1_STEP_UI_GROUNDING = "4";
export const taskRef = (taskId) => `Task ${taskId}`;
export const taskStepRef = (taskId, stepId) => `Task ${taskId} Step ${stepId}`;
