/**
 * 问题五十二: Local Docker / Browser Acceptance — 本地 Docker 与浏览器验收契约。
 *
 * 强制执行:
 * - 有可运行前端/全栈服务时不得只停在单测、静态检查或报告
 * - 浏览器验收失败必须 capture/classify/map/repair/rebuild/redeploy/rerun
 * - 最终必须给用户本地可访问 URL，否则不得 delivered
 * - 不得用空 errors 数组模拟 passed，必须有真实 HTTP 请求证据
 */
import { errorMessage } from "../core/helpers.js";
/**
 * 创建本地验收契约。
 */
export function createLocalDockerAcceptanceContract(overrides) {
    return {
        project_run_mode: "not_applicable",
        local_acceptance_status: "not_applicable",
        required_services: [],
        build_artifacts: [],
        browser_acceptance_plan: {
            target_urls: [],
            user_flows: [],
            viewport_matrix: ["desktop"],
            required_assertions: [],
            screenshot_or_trace_required: false,
        },
        repair_loop_policy: {
            max_iterations: 3,
            rebuild_required_after_fix: true,
            redeploy_required_after_fix: true,
            retest_required_after_fix: true,
            stop_conditions: ["retry_budget_exhausted", "same_error_repeated"],
        },
        local_runtime_evidence: {
            build_command: "",
            image_ids: [],
            build_artifacts: [],
            container_ids: [],
            service_urls: [],
            health_check_outputs: [],
            logs_summary_zh: [],
        },
        browser_acceptance_evidence: [],
        final_access: {
            frontend_urls: [],
            backend_urls: [],
            docs_or_swagger_urls: [],
            start_commands: [],
            stop_commands: [],
            running_status: "not_started",
            manual_review_steps_zh: [],
            known_limits_zh: [],
        },
        ...overrides,
    };
}
/**
 * 评估是否需要本地验收。
 */
export function requiresLocalAcceptance(hasFrontend, hasBackend, hasDockerCompose, hasDockerfile) {
    if (hasDockerCompose) {
        return { required: true, run_mode: "docker_compose" };
    }
    if (hasDockerfile) {
        return { required: true, run_mode: "dockerfile" };
    }
    if (hasFrontend || hasBackend) {
        return { required: true, run_mode: "local_dev_server" };
    }
    return { required: false, run_mode: "not_applicable" };
}
/**
 * 创建服务运行规格。
 */
export function createServiceRuntimeSpec(overrides) {
    return {
        service_type: "backend_api",
        ports: [],
        health_check: "http://localhost/health",
        required_env: [],
        ...overrides,
    };
}
/**
 * 执行真实本地 HTTP 验收 — 必须有真实 fetch 证据。
 * 对指定 URL 执行 HTTP GET，记录 status、response body 片段、断言结果。
 */
export async function executeRealHttpAcceptance(targetUrl, assertions) {
    const timestamp = new Date().toISOString();
    const runHash = `${timestamp}:${targetUrl}`;
    try {
        const response = await fetch(targetUrl);
        const httpStatus = response.status;
        const body = await response.text();
        const bodySample = body.slice(0, 500);
        const assertionResults = assertions.map(a => `${a.name}: ${a.check(httpStatus, bodySample) ? "PASS" : "FAIL"}`);
        return {
            health_check_ok: httpStatus >= 200 && httpStatus < 400,
            health_check_output: `HTTP ${httpStatus} from ${targetUrl}`,
            target_url: targetUrl,
            assertion_results: assertionResults,
            http_status: httpStatus,
            response_body_sample: bodySample,
            timestamp,
            run_hash: runHash,
        };
    }
    catch (err) {
        const errorMsg = errorMessage(err);
        return {
            health_check_ok: false,
            health_check_output: `FETCH_ERROR: ${errorMsg}`,
            target_url: targetUrl,
            assertion_results: [`fetch: FAIL (${errorMsg})`],
            http_status: null,
            response_body_sample: "",
            timestamp,
            run_hash: runHash,
        };
    }
}
/**
 * 使用可注入 transport 执行 HTTP 验收 — 不依赖端口绑定。
 * transport 是一个 fetch-like 函数: (url) → { status, text() }
 * 用于单元/Release Gate 中验证完整证据链，无需监听端口。
 */
export async function executeHttpAcceptanceWithTransport(targetUrl, assertions, transport) {
    const timestamp = new Date().toISOString();
    const runHash = `${timestamp}:${targetUrl}`;
    try {
        const response = await transport(targetUrl);
        const httpStatus = response.status;
        const body = await response.text();
        const bodySample = body.slice(0, 500);
        const assertionResults = assertions.map(a => `${a.name}: ${a.check(httpStatus, bodySample) ? "PASS" : "FAIL"}`);
        return {
            health_check_ok: httpStatus >= 200 && httpStatus < 400,
            health_check_output: `HTTP ${httpStatus} from ${targetUrl}`,
            target_url: targetUrl,
            assertion_results: assertionResults,
            http_status: httpStatus,
            response_body_sample: bodySample,
            timestamp,
            run_hash: runHash,
        };
    }
    catch (err) {
        const errorMsg = errorMessage(err);
        return {
            health_check_ok: false,
            health_check_output: `FETCH_ERROR: ${errorMsg}`,
            target_url: targetUrl,
            assertion_results: [`fetch: FAIL (${errorMsg})`],
            http_status: null,
            response_body_sample: "",
            timestamp,
            run_hash: runHash,
        };
    }
}
/**
 * 将真实验收结果转换为浏览器验收证据。
 */
export function realResultToBrowserEvidence(result, flowName) {
    return {
        target_url: result.target_url,
        flow_name: flowName,
        browser_steps: [`HTTP GET ${result.target_url} → ${result.http_status ?? "error"}`],
        assertion_results: result.assertion_results,
        screenshot_refs: [],
        trace_refs: [],
        console_errors: result.health_check_ok ? [] : [result.health_check_output],
        network_failures: result.http_status === null ? [result.health_check_output] : [],
        acceptance_status: result.health_check_ok && result.assertion_results.every(r => r.endsWith(": PASS"))
            ? "passed"
            : "failed",
    };
}
/**
 * 执行浏览器验收 — 失败必须 capture/classify/map/repair/rebuild/redeploy/rerun。
 * 注意: 此函数仅分类已知错误，不执行真实 HTTP 请求。
 * 真实验收请使用 executeRealHttpAcceptance + realResultToBrowserEvidence。
 */
export function performBrowserAcceptance(plan, errors, _repairPolicy) {
    // 不得仅凭空 errors 直接判定 passed
    if (errors.length === 0) {
        return {
            evidence: {
                target_url: plan.target_urls[0] ?? "",
                flow_name: plan.user_flows[0] ?? "",
                browser_steps: [],
                assertion_results: [],
                screenshot_refs: [],
                trace_refs: [],
                console_errors: [],
                network_failures: [],
                acceptance_status: "failed",
            },
            needsRepair: false,
            stopReason: "空 errors 不得直接判定 passed，必须使用 executeRealHttpAcceptance 提供真实证据",
        };
    }
    return {
        evidence: {
            target_url: plan.target_urls[0] ?? "",
            flow_name: plan.user_flows[0] ?? "",
            browser_steps: [],
            assertion_results: [],
            screenshot_refs: [],
            trace_refs: [],
            console_errors: [...errors],
            network_failures: [],
            acceptance_status: "failed",
        },
        needsRepair: true,
        stopReason: "浏览器验收失败，需要修复",
    };
}
/**
 * 验证最终访问 — 必须给用户本地可访问 URL。
 */
export function validateFinalAccess(finalAccess, projectRunMode) {
    const blockingReasons = [];
    if (projectRunMode === "not_applicable") {
        return { passed: true, status: "not_applicable", blocking_reasons: [] };
    }
    if (finalAccess.frontend_urls.length === 0 && finalAccess.backend_urls.length === 0) {
        blockingReasons.push("最终必须给用户本地可访问 URL");
    }
    const allUrls = [...finalAccess.frontend_urls, ...finalAccess.backend_urls];
    for (const url of allUrls) {
        if (!/^https?:\/\/(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?/i.test(url)) {
            blockingReasons.push(`URL "${url}" 不是本地可访问地址`);
        }
    }
    if (finalAccess.start_commands.length === 0) {
        blockingReasons.push("缺少启动命令");
    }
    if (finalAccess.running_status !== "running" && finalAccess.running_status !== "passed") {
        blockingReasons.push(`服务运行状态为 "${finalAccess.running_status}"，期望 running/passed`);
    }
    return {
        passed: blockingReasons.length === 0,
        status: blockingReasons.length === 0 ? "passed" : "failed",
        blocking_reasons: blockingReasons,
    };
}
/**
 * 验证本地验收全量证据 — 检查 health check、browser evidence、build artifacts。
 * 不允许仅靠传入空 errors 数组判定成功。
 */
export function validateLocalAcceptanceEvidence(contract) {
    const blockingReasons = [];
    if (contract.project_run_mode === "not_applicable") {
        return { passed: true, status: "not_applicable", blocking_reasons: [] };
    }
    const runtimeEvidence = contract.local_runtime_evidence;
    if (!runtimeEvidence.health_check_outputs || runtimeEvidence.health_check_outputs.length === 0) {
        blockingReasons.push("缺少 health check 输出，服务健康未验证");
    }
    if (!runtimeEvidence.service_urls || runtimeEvidence.service_urls.length === 0) {
        blockingReasons.push("缺少 service_urls，无法确认服务可访问");
    }
    if ((!runtimeEvidence.build_artifacts || runtimeEvidence.build_artifacts.length === 0) &&
        (!runtimeEvidence.image_ids || runtimeEvidence.image_ids.length === 0)) {
        blockingReasons.push("缺少 build_artifacts 或 image_ids，无法确认使用了最新构建");
    }
    const browserEvidence = contract.browser_acceptance_evidence;
    if (!browserEvidence || browserEvidence.length === 0) {
        blockingReasons.push("缺少 browser_acceptance_evidence，未执行浏览器/HTTP 验收");
    }
    else {
        const lastEvidence = browserEvidence[browserEvidence.length - 1];
        if (lastEvidence.acceptance_status !== "passed") {
            blockingReasons.push(`最终浏览器验收状态为 "${lastEvidence.acceptance_status}"，期望 passed`);
        }
        if (!lastEvidence.target_url) {
            blockingReasons.push("浏览器验收证据缺少 target_url");
        }
        if (!lastEvidence.assertion_results || lastEvidence.assertion_results.length === 0) {
            blockingReasons.push("浏览器验收证据缺少 assertion_results，未记录断言结果");
        }
        // 必须有真实 HTTP 证据: browser_steps 非空（记录了实际 HTTP 请求）
        if (!lastEvidence.browser_steps || lastEvidence.browser_steps.length === 0) {
            blockingReasons.push("浏览器验收证据缺少 browser_steps，未记录真实 HTTP 请求");
        }
    }
    // 最终访问验证
    const finalResult = validateFinalAccess(contract.final_access, contract.project_run_mode);
    blockingReasons.push(...finalResult.blocking_reasons);
    return {
        passed: blockingReasons.length === 0,
        status: blockingReasons.length === 0 ? "passed" : "failed",
        blocking_reasons: blockingReasons,
    };
}
/**
 * 评估修复循环策略。
 */
export function evaluateRepairLoop(iteration, policy, lastErrors, previousErrors) {
    if (iteration >= policy.max_iterations) {
        return { continue_repair: false, reason: "修复循环超过最大迭代次数" };
    }
    if (lastErrors.length > 0 && previousErrors.length > 0) {
        const sameErrors = lastErrors.every(e => previousErrors.includes(e));
        if (sameErrors) {
            return { continue_repair: false, reason: "same_error_repeated: 修复未生效，停止循环" };
        }
    }
    return { continue_repair: true, reason: `修复循环第 ${iteration + 1}/${policy.max_iterations} 次` };
}
//# sourceMappingURL=local_docker_acceptance.js.map