import fs from "node:fs";
import path from "node:path";
import YAML from "yaml";
import { createConfigEvidenceEntry } from "./config_precedence_contract/evidence.js";
import { debug } from "../core/logger.js";
import { errorMessage, safeJsonParse } from "../core/helpers.js";
const EVIDENCE_FIELD_PATHS = [
    "name",
    "tech_stack.backend",
    "tech_stack.frontend",
    "build_commands.backend",
    "build_commands.frontend",
    "product_profile",
    "repos",
    "scope.backend",
    "scope.frontend",
];
function cloneConfig(raw) {
    if (!raw || typeof raw !== "object" || Array.isArray(raw))
        return {};
    return structuredClone(raw);
}
function getField(config, fieldPath) {
    let current = config;
    for (const part of fieldPath.split(".")) {
        if (!current || typeof current !== "object")
            return undefined;
        current = current[part];
    }
    return current;
}
function setField(config, fieldPath, value) {
    const parts = fieldPath.split(".");
    let current = config;
    for (let i = 0; i < parts.length - 1; i++) {
        const part = parts[i];
        if (!current[part] || typeof current[part] !== "object")
            current[part] = {};
        current = current[part];
    }
    current[parts[parts.length - 1]] = value;
}
function normalizeStringField(config, fieldPath, issues) {
    const value = getField(config, fieldPath);
    if (typeof value === "number" || typeof value === "boolean") {
        const nextValue = String(value);
        setField(config, fieldPath, nextValue);
        issues.push({
            kind: "schema_type",
            field_path: fieldPath,
            message_zh: `${fieldPath} 是机械类型错误，已转为字符串`,
            before: value,
            after: nextValue,
        });
    }
}
function normalizeStringArrayField(config, fieldPath, issues) {
    const value = getField(config, fieldPath);
    if (typeof value === "string") {
        const nextValue = value.trim() ? [value] : [];
        setField(config, fieldPath, nextValue);
        issues.push({
            kind: "schema_type",
            field_path: fieldPath,
            message_zh: `${fieldPath} 是机械类型错误，已转为数组`,
            before: value,
            after: nextValue,
        });
    }
}
function normalizeRepoScopes(config, issues) {
    const repos = config.repos;
    if (!Array.isArray(repos))
        return;
    repos.forEach((repo, index) => {
        if (repo && typeof repo === "object" && !Array.isArray(repo) && typeof repo.scope === "string") {
            const repoRecord = repo;
            const before = repoRecord.scope;
            repoRecord.scope = before.trim() ? [before] : [];
            issues.push({
                kind: "schema_type",
                field_path: `repos.${index}.scope`,
                message_zh: `repos.${index}.scope 是机械类型错误，已转为数组`,
                before,
                after: repoRecord.scope,
            });
        }
    });
}
function normalizeMechanicalTypes(raw) {
    const config = cloneConfig(raw);
    const issues = [];
    normalizeStringField(config, "tech_stack.backend.version", issues);
    normalizeStringField(config, "tech_stack.frontend.version", issues);
    normalizeStringArrayField(config, "scope.backend", issues);
    normalizeStringArrayField(config, "scope.frontend", issues);
    normalizeRepoScopes(config, issues);
    return { config, issues };
}
// [DC-5] _hashValue 已去重 — 使用 config_precedence_contract/evidence.ts 的 hashValue
export { hashValue as _hashValue } from "./config_precedence_contract/evidence.js";
function buildEvidencePayload(projectPath, config) {
    const now = new Date().toISOString();
    const fields = {};
    const entries = [];
    for (const fieldPath of EVIDENCE_FIELD_PATHS) {
        const value = getField(config, fieldPath);
        if (value === undefined)
            continue;
        fields[fieldPath] = {
            value,
            confidence: "high",
            confidence_source: "confirmed",
            evidence: ["config repair"],
        };
        entries.push(createConfigEvidenceEntry(fieldPath, value, "confirmed_config", {
            confidence: 1.0,
            evidence_refs: ["config repair"],
            confirmed_by: process.env.USER || "cli",
        }));
    }
    return {
        schema_version: 2,
        generated_at: now,
        updated_at: now,
        mode: "repair",
        project_root: projectPath,
        fields,
        entries,
    };
}
function evidenceIssues(_projectPath, configPath, evidencePath, config) {
    const issues = [];
    if (!fs.existsSync(evidencePath)) {
        issues.push({
            kind: "evidence_missing",
            field_path: "config.evidence.json",
            message_zh: "config.evidence.json 缺失，可根据当前 config.yaml 自动重签",
        });
        return issues;
    }
    try {
        const configStat = fs.statSync(configPath);
        const evidenceStat = fs.statSync(evidencePath);
        if (configStat.mtimeMs > evidenceStat.mtimeMs) {
            issues.push({
                kind: "evidence_stale",
                field_path: "config.evidence.json",
                message_zh: "config.evidence.json 已过期，可根据当前 config.yaml 自动重签",
            });
        }
    }
    catch {
        debug("config_auto_repair", "配置证据检查失败，跳过自动修复");
    }
    try {
        const raw = safeJsonParse(fs.readFileSync(evidencePath, "utf-8"));
        if (!raw)
            throw new Error("evidence JSON 无法解析");
        const fields = raw.fields ?? {};
        for (const [fieldPath, field] of Object.entries(fields)) {
            const actual = getField(config, fieldPath);
            if (actual !== undefined && JSON.stringify(field.value) !== JSON.stringify(actual)) {
                issues.push({
                    kind: "evidence_mismatch",
                    field_path: fieldPath,
                    message_zh: `${fieldPath} 的 evidence 值与 config.yaml 不一致，可自动重签`,
                    before: field.value,
                    after: actual,
                });
            }
        }
    }
    catch {
        issues.push({
            kind: "evidence_mismatch",
            field_path: "config.evidence.json",
            message_zh: "config.evidence.json 无法解析，可根据当前 config.yaml 自动重签",
        });
    }
    return issues;
}
export function analyzeConfigRepair(projectPath) {
    const configPath = path.join(projectPath, ".soloforge", "config.yaml");
    const evidencePath = path.join(projectPath, ".soloforge", "config.evidence.json");
    if (!fs.existsSync(configPath)) {
        return {
            project_path: projectPath,
            config_path: configPath,
            evidence_path: evidencePath,
            config_exists: false,
            parse_error: null,
            issues: [],
            normalized_config: null,
            needs_repair: false,
            auto_resolvable: false,
        };
    }
    let parsed;
    try {
        parsed = YAML.parse(fs.readFileSync(configPath, "utf-8")) ?? {};
    }
    catch (error) {
        return {
            project_path: projectPath,
            config_path: configPath,
            evidence_path: evidencePath,
            config_exists: true,
            parse_error: errorMessage(error),
            issues: [],
            normalized_config: null,
            needs_repair: false,
            auto_resolvable: false,
        };
    }
    const normalized = normalizeMechanicalTypes(parsed);
    const issues = [
        ...normalized.issues,
        ...evidenceIssues(projectPath, configPath, evidencePath, normalized.config),
    ];
    return {
        project_path: projectPath,
        config_path: configPath,
        evidence_path: evidencePath,
        config_exists: true,
        parse_error: null,
        issues,
        normalized_config: normalized.config,
        needs_repair: issues.length > 0,
        auto_resolvable: issues.length > 0 && issues.every((issue) => issue.kind === "schema_type" || issue.kind === "evidence_missing" || issue.kind === "evidence_stale" || issue.kind === "evidence_mismatch"),
    };
}
function backupFile(filePath, backupDir) {
    if (!fs.existsSync(filePath))
        return null;
    fs.mkdirSync(backupDir, { recursive: true });
    const stamp = new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
    const backupPath = path.join(backupDir, `${path.basename(filePath)}.${stamp}.bak`);
    fs.copyFileSync(filePath, backupPath);
    return backupPath;
}
export function repairProjectConfig(projectPath, options = {}) {
    const analysis = analyzeConfigRepair(projectPath);
    const backupPaths = [];
    if (!analysis.config_exists || analysis.parse_error || !analysis.normalized_config || !analysis.needs_repair || options.dry_run) {
        return { ...analysis, dry_run: !!options.dry_run, changed: false, backup_paths: backupPaths };
    }
    const backupDir = path.join(projectPath, ".soloforge", "config-backups");
    const configBackup = backupFile(analysis.config_path, backupDir);
    const evidenceBackup = backupFile(analysis.evidence_path, backupDir);
    if (configBackup)
        backupPaths.push(configBackup);
    if (evidenceBackup)
        backupPaths.push(evidenceBackup);
    fs.writeFileSync(analysis.config_path, YAML.stringify(analysis.normalized_config), "utf-8");
    fs.writeFileSync(analysis.evidence_path, JSON.stringify(buildEvidencePayload(projectPath, analysis.normalized_config), null, 2), "utf-8");
    return { ...analysis, dry_run: false, changed: true, backup_paths: backupPaths };
}
//# sourceMappingURL=config_auto_repair.js.map