/**
 * 用户项目代码可维护性与可观测性契约。
 *
 * 治理对象是用户项目代码，不是 SoloForge 自身。
 * 覆盖必要注释、结构化日志、错误日志、审计日志、
 * 敏感信息脱敏和低风险跳过。
 */
/* ── 关键词 ─────────────────────────────────────── */
const OBSERVABILITY_KEYWORDS = /业务|支付|金额|账单|合同|业务办理|权限|登录|安全|通知|异步|状态机|审批|折扣|核验|外部系统|webhook|回调|异常处理|重试|补偿|事务|幂等|并发|数据修复|批处理|迁移|Controller|Service|UseCase|Domain|Repository|payment|billing|invoice|contract|permission|auth|security|state|workflow|migration|refund|charge|order/i;
const BUSINESS_IMPLEMENTATION_KEYWORDS = /实现|新增|创建|更新|删除|保存|提交|接口|API|Controller|Service|Repository|Mapper|DTO|Entity|UseCase|页面提交|表单提交|业务逻辑|业务规则|CRUD|create|update|delete|save|submit|endpoint|handler|controller|service|repository|usecase/i;
const DESIGN_ARTIFACT_KEYWORDS = /设计|文档|规格|OpenAPI|API接口规格|数据库设计|架构设计|接口设计|artifact|spec/i;
const CODE_IMPLEMENTATION_KEYWORDS = /实现|新增|创建|更新|删除|保存|提交|编码|代码|Controller|Service|Repository|Mapper|DTO|Entity|UseCase|CRUD|create|update|delete|save|submit|handler|controller|service|repository|usecase/i;
const LOW_RISK_KEYWORDS = /错别字|拼写|文案|样式|css|颜色|README|配置值|测试数据|fixture|getter|setter|简单类型|类型定义|interface\s+\w+\s*\{|type\s+\w+\s*=/i;
const CRITICAL_LOG_KEYWORDS = /支付|账单|金额|退款|核验|折扣|缴费|payment|billing|refund|charge|invoice/i;
const SECURITY_LOG_KEYWORDS = /权限|登录|认证|越权|安全|role|auth|permission|security|access.?denied|forbidden/i;
const STATE_CHANGE_KEYWORDS = /状态|流转|status|state|transition|pending|approved|rejected|cancelled/i;
const EXTERNAL_CALL_KEYWORDS = /外部|第三方|API|调用|HTTP|RPC|webhook|callback|RestTemplate|WebClient|fetch|axios/i;
const MIGRATION_KEYWORDS = /迁移|修复|批处理|migration|batch|data.?fix|script/i;
const COMPLEX_RULE_KEYWORDS = /金额计算|计算逻辑|领域不变量|状态机|幂等|并发控制|分布式锁|算法|formula|invariant|idempoten|concurr/i;
const BACKEND_IMPLEMENTATION_FILE = /(?:Controller|Service|ServiceImpl|UseCase|Facade|ApplicationService|DomainService|Mapper|Repository|Resolver|Handler|Route|Router|ViewSet|Resource|Endpoint)\.(?:java|kt|ts|tsx|js|mjs|cjs|py|go|rb|cs)$/i;
const BACKEND_IMPLEMENTATION_PATH = /(?:^|\/)(?:controllers?|services?|usecases?|application|domain|routes?|routers?|handlers?|resolvers?|repositories?|mappers?|views?|api|endpoints?)\//i;
const BACKEND_DATA_CONTRACT_FILE = /(?:Entity|DTO|Dto|Request|Response|VO|Vo|CreateReq|UpdateReq|Command|Query|Schema|Payload|Input|Output|Serializer|Model)\.(?:java|kt|ts|tsx|js|mjs|cjs|py|go|rb|cs)$/i;
const BACKEND_DATA_CONTRACT_PATH = /(?:^|\/)(?:dto|dtos|schemas?|payloads?|requests?|responses?|serializers?|models?|entities?)\//i;
const FRONTEND_IMPLEMENTATION_FILE = /(?:Page|View|Screen|Form|Panel|Table|List|Modal|Dialog|Store|Slice|Composable|Hook|Service|Client)\.(?:vue|tsx|jsx|ts|js|svelte)$/i;
const FRONTEND_IMPLEMENTATION_PATH = /(?:^|\/)(?:pages?|views?|screens?|components?|stores?|hooks?|composables?|clients?|services?)\//i;
const BUSINESS_WRITE_ENDPOINT = /@(PostMapping|PutMapping|PatchMapping|DeleteMapping)|\b(?:router|app|server)\.(?:post|put|patch|delete)\s*\(|\b(?:post|put|patch|delete)\s*\(\s*["'`/]|@(?:app|router)\.(?:post|put|patch|delete)\s*\(|\b(?:def|async\s+def)\s+(?:create|update|delete|remove|patch|post|put)_|\bfunc\s+(?:Create|Update|Delete|Remove|Patch|Post|Put)\w*\s*\(|\b(?:create|add|save|insert|update|edit|delete|remove|disable|enable|reset|assign|bind|unbind|approve|reject|submit|cancel|terminate|status|transition|leave|offboard)\w*\s*\(/i;
const BUSINESS_WRITE_CALL = /\.(?:save|insert|update|delete|remove|disable|enable|reset|assign|bind|unbind|approve|reject|submit|cancel|terminate|transition|create|patch)\w*\s*\(|\b(?:db|repo|repository|mapper|dao|client)\.(?:Save|Insert|Update|Delete|Create|Patch|Exec|QueryRow)\w*\s*\(|\b(?:session|db)\.(?:add|delete|commit|execute)\s*\(/i;
const FRONTEND_MUTATION_CALL = /(?:axios|fetch|request|http|apiClient|client)\.(?:post|put|patch|delete)\s*\(|\bfetch\s*\([^)]*method\s*:\s*["'`](?:POST|PUT|PATCH|DELETE)|\b(?:submit|save|create|update|delete|remove|disable|enable|reset|assign|approve|reject|cancel|pay|refund|upload)\w*\s*\(/i;
const FRONTEND_STATE_MUTATION = /(?:setState|useState|useReducer|dispatch|commit|store\.|zustand|pinia|redux|set[A-Z]\w*|status\s*=|\.value\s*=)/i;
const TRANSACTIONAL_OR_CASCADE_KEYWORDS = /@Transactional|事务|双写|级联|cascade|状态转换|状态流转|停用|密码重置|角色分配|权限分配|sys[_-]?user|userMapper|accountStatus|resourceStatus|accountStatus|status transition/i;
const FIELD_DOC_ANNOTATION = /(?:\/\/|\/\*\*?|^\s*\*|#|@Schema|@ApiModelProperty|@Parameter|@Column\(.*comment|@Comment|Field\(|description\s*=|json_schema_extra|pydantic\.Field)/i;
const CJK_TEXT = /[\u4e00-\u9fff]/;
const JAVA_SOURCE_FILE = /\.java$/i;
const JAVA_TYPE_DECLARATION = /\b(?:public\s+)?(?:abstract\s+|final\s+)?(?:class|interface|enum|record)\s+(\w+)/;
const JAVA_METHOD_DECLARATION = /^\s*(?:public|protected|private)\s+(?:static\s+|final\s+|synchronized\s+|abstract\s+)*(?:<[^>]+>\s*)?([\w<>[\]?.,\s]+)\s+(\w+)\s*\(([^)]*)\)\s*(?:throws\s+[\w.,\s]+)?\s*\{/;
const IMPORTANT_BACKEND_LINE = /(?:\.insert\s*\(|\.update\s*\(|\.delete\s*\(|\.remove\s*\(|\.save\s*\(|setStatus\s*\(|updateStatus\s*\(|assignRole\s*\(|resetPassword\s*\(|sysUserMapper|userMapper|paymentGateway|externalClient|permissionService|auditMapper)/i;
function isBackendImplementationFile(file) {
    return BACKEND_IMPLEMENTATION_FILE.test(file) || BACKEND_IMPLEMENTATION_PATH.test(file);
}
function isBackendDataContractFile(file) {
    return BACKEND_DATA_CONTRACT_FILE.test(file) || BACKEND_DATA_CONTRACT_PATH.test(file);
}
function isFrontendImplementationFile(file) {
    return FRONTEND_IMPLEMENTATION_FILE.test(file) || FRONTEND_IMPLEMENTATION_PATH.test(file) || /\.(?:vue|tsx|jsx|svelte)$/i.test(file);
}
function containsChinese(text) {
    return CJK_TEXT.test(text);
}
function isProductionJavaBackendFile(file) {
    return JAVA_SOURCE_FILE.test(file) && (isBackendImplementationFile(file) || isBackendDataContractFile(file));
}
function extractJavadocBefore(lines, lineIndex, windowSize = 10) {
    const start = Math.max(0, lineIndex - windowSize);
    const window = lines.slice(start, lineIndex).join("\n");
    const matches = [...window.matchAll(/\/\*\*[\s\S]*?\*\//g)];
    return matches.at(-1)?.[0];
}
function javaParams(paramText) {
    return paramText
        .split(",")
        .map((p) => p.trim())
        .filter(Boolean)
        .map((p) => p.replace(/@\w+(?:\([^)]*\))?\s*/g, "").trim().split(/\s+/).at(-1) ?? "")
        .filter((p) => p.length > 0);
}
function hasNearbyChineseLineComment(lines, lineIndex) {
    const nearby = lines.slice(Math.max(0, lineIndex - 3), lineIndex + 1).join("\n");
    return /\/\//.test(nearby) && containsChinese(nearby);
}
const SENSITIVE_PATTERNS = [
    { pattern: /(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key)\s*[:=]\s*["'][^"']{4,}/i, name_zh: "明文密钥/令牌" },
    { pattern: /(?:cookie|session[_-]?id|set[_-]?cookie)\s*[:=]\s*["'][^"']{8,}/i, name_zh: "Cookie/会话" },
    { pattern: /(?:手机号|phone|mobile|cell)\s*[:=]\s*["']?\d{11}/i, name_zh: "手机号全量" },
    { pattern: /(?:身份证|id[_-]?card|id[_-]?number)\s*[:=]\s*["']?\d{15,18}/i, name_zh: "身份证全量" },
    { pattern: /(?:银行卡|bank[_-]?card|card[_-]?number|pan)\s*[:=]\s*["']?\d{13,19}/i, name_zh: "银行卡全量" },
    { pattern: /(?:健康|病历|诊断|处方|health|medical|diagnosis|prescription)/i, name_zh: "健康隐私" },
];
// 日志输出中的敏感信息 — 区分声明与输出
const LOG_OUTPUT_SENSITIVE_PATTERNS = [
    // logger.info({ token }) / console 输出含 password 字段的对象
    { pattern: /(?:log|logger|LOG|console)\.(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\(\s*\{[^}]*(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|cookie|session[_-]?id)\b/i, name_zh: "日志对象含敏感字段" },
    // console dot log 输出 user.password / log.info(data.secret)
    { pattern: /(?:log|logger|LOG|console)\.(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\([^)]*\b\w+\.(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|cookie|session[_-]?id)\b/i, name_zh: "日志输出敏感属性值" },
    // logger.info("token={}", token) / log.info("password=" + pwd) / log.info("token: %s", t)
    { pattern: /(?:log|logger|LOG|console)\.(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\(\s*["'][^"']*\b(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|cookie|session[_-]?id)\s*[=：:]\s*(?:["']?\s*\+|\{?\}|%[sd])/i, name_zh: "日志拼接/格式化敏感值" },
];
/* ── 触发判断 ─────────────────────────────────── */
export function requiresCodeObservabilityContract(intent, route, changedFiles = []) {
    const text = `${intent} ${changedFiles.join(" ")}`;
    if (route === "artifact_generation" && DESIGN_ARTIFACT_KEYWORDS.test(intent) && !CODE_IMPLEMENTATION_KEYWORDS.test(intent))
        return false;
    const hasObservabilitySignal = OBSERVABILITY_KEYWORDS.test(text) || BUSINESS_IMPLEMENTATION_KEYWORDS.test(text);
    if (LOW_RISK_KEYWORDS.test(text) && !hasObservabilitySignal)
        return false;
    const relevantRoute = !route
        || ["code_change", "artifact_generation", "operation", "multi_stage_plan"].includes(route);
    if (!relevantRoute)
        return false;
    if (!hasObservabilitySignal)
        return false;
    // 简单修复/调试/查询不触发完整契约
    if (/^(?:修复|调试|排查|查看|分析|解释|为什么|查一下|帮我看看|debug|fix\s+(?:a\s+)?(?:typo|lint|import|build))/i.test(intent.trim()))
        return false;
    return true;
}
/* ── 项目 logger 检测 ──────────────────────────── */
export function detectProjectLogger(fileContents) {
    const allContent = Object.values(fileContents).join("\n");
    if (/import\s+.*(?:LoggerFactory|@Slf4j|log\s*=.*Logger)/.test(allContent) || /log\.(info|warn|error|debug)\s*\(/.test(allContent)) {
        return { type: "slf4j", import_pattern: "LoggerFactory.getLogger", call_pattern: "log.info/warn/error", confidence: "high" };
    }
    if (/import\s+.*(?:pino|from\s+['"]pino['"])/.test(allContent) || /(?:const|let)\s+\w*(?:logger|log)\s*=\s*pino/.test(allContent)) {
        return { type: "pino", import_pattern: "import pino", call_pattern: "logger.info/warn/error", confidence: "high" };
    }
    if (/import\s+.*(?:winston|from\s+['"]winston['"])/.test(allContent) || /createLogger\s*\(/.test(allContent)) {
        return { type: "winston", import_pattern: "import winston", call_pattern: "logger.info/warn/error", confidence: "high" };
    }
    if (/import\s+.*(?:@nestjs|NestJS|Logger\s*}\s*from\s+['"]@nestjs)/.test(allContent)) {
        return { type: "nestjs_logger", import_pattern: "import { Logger } from '@nestjs/common'", call_pattern: "this.logger.log/warn/error", confidence: "high" };
    }
    const fileKeys = Object.keys(fileContents).join(" ");
    const hasFrontendExt = /\.(?:vue|tsx|jsx|svelte)\b/.test(fileKeys);
    const hasFrontendImport = /\.vue\s*['"]|\.tsx\s*['"]|\.jsx\s*['"]|react['"]|vue['"]/.test(allContent);
    if (hasFrontendExt || hasFrontendImport) {
        if (/(?:const|let)\s+\w*(?:logger|Logger)\s*=/.test(allContent)) {
            return { type: "custom_logger", import_pattern: "项目封装 logger", call_pattern: "logger.info/warn/error", confidence: "medium" };
        }
        return { type: "frontend_project", import_pattern: "前端项目", call_pattern: "生产环境禁止 console", confidence: "medium" };
    }
    if (/(?:const|let)\s+\w*(?:logger|Logger)\s*=/.test(allContent)) {
        return { type: "custom_logger", import_pattern: "项目封装 logger", call_pattern: "logger.info/warn/error", confidence: "medium" };
    }
    return { type: "none_detected", import_pattern: "无", call_pattern: "console.log", confidence: "low" };
}
/* ── 工作包创建 ───────────────────────────────── */
export function createCodeObservabilityWorkPackage(taskId, intent, loggerInfo) {
    const text = intent;
    const requiredLogs = [];
    const sensitiveFields = [];
    const requiredComments = [];
    if (CRITICAL_LOG_KEYWORDS.test(text)) {
        requiredLogs.push("支付/金额/退款/核验状态变更必须有业务日志，含对象 ID、操作人、前后状态");
        sensitiveFields.push("payment_amount", "card_number", "bank_account");
    }
    if (SECURITY_LOG_KEYWORDS.test(text)) {
        requiredLogs.push("权限拒绝/越权访问必须有安全日志，含操作人、目标资源、拒绝原因");
    }
    if (STATE_CHANGE_KEYWORDS.test(text)) {
        requiredLogs.push("关键状态流转必须有日志，含流转前后状态和触发原因");
    }
    if (EXTERNAL_CALL_KEYWORDS.test(text)) {
        requiredLogs.push("外部调用失败/超时/重试必须有日志，含目标、响应码/错误、重试次数");
    }
    if (MIGRATION_KEYWORDS.test(text)) {
        requiredLogs.push("数据修复/迁移脚本必须有审计日志，含修复前值、修复后值、影响行数");
    }
    if (COMPLEX_RULE_KEYWORDS.test(text)) {
        requiredComments.push("复杂业务规则必须有注释说明原因（为什么），不得只复述代码行为（做什么）");
    }
    if (/事务|幂等|并发|分布式锁/i.test(text)) {
        requiredComments.push("事务边界、幂等策略、并发控制必须有注释说明设计意图");
    }
    return {
        task_id: taskId,
        status: "required",
        applicability_reason_zh: `该任务涉及业务编码，需确认代码可维护性与可观测性约束: ${intent}`,
        required_logs: requiredLogs,
        required_comments: requiredComments,
        sensitive_fields: sensitiveFields,
        detected_logger: loggerInfo,
    };
}
function workPackageSatisfied(wp) {
    return !!wp && wp.status === "satisfied";
}
/* ── Gate ─────────────────────────────────────── */
export function evaluateCodeObservabilityGate(input) {
    if (!requiresCodeObservabilityContract(input.intent, input.route, input.changed_files)) {
        return {
            applicable: false,
            allowed: true,
            status: "not_applicable",
            reason_zh: "低风险或非业务编码任务，不触发代码可维护性/可观测性工作包",
            findings: [],
        };
    }
    const loggerInfo = input.project_files
        ? detectProjectLogger(input.project_files)
        : undefined;
    const wp = input.work_package ?? createCodeObservabilityWorkPackage(input.task_id, input.intent, loggerInfo);
    if (!workPackageSatisfied(wp)) {
        return {
            applicable: true,
            allowed: false,
            status: "blocked",
            reason_zh: "业务编码任务缺少代码可维护性/可观测性工作包，不得开始实现",
            work_package: { ...wp, detected_logger: loggerInfo },
            findings: [],
        };
    }
    return {
        applicable: true,
        allowed: true,
        status: "satisfied",
        reason_zh: "代码可维护性/可观测性工作包已具备，可进入实现",
        work_package: { ...wp, detected_logger: loggerInfo },
        findings: [],
    };
}
/* ── Review: 敏感信息检测 ─────────────────────── */
export function detectSensitiveLogs(fileContents) {
    const findings = [];
    for (const [file, content] of Object.entries(fileContents)) {
        if (/\.(?:test|spec)\./.test(file))
            continue;
        if (isProductionJavaBackendFile(file)) {
            const lines = content.split(/\r?\n/);
            const typeMatch = content.match(JAVA_TYPE_DECLARATION);
            const typeName = typeMatch?.[1];
            for (let i = 0; i < lines.length; i++) {
                const typeLine = lines[i] ?? "";
                const typeDecl = typeLine.match(JAVA_TYPE_DECLARATION);
                if (!typeDecl)
                    continue;
                const doc = extractJavadocBefore(lines, i);
                if (!doc || !containsChinese(doc)) {
                    findings.push({
                        category: "missing_class_doc",
                        severity: "hard_fail",
                        file,
                        evidence: `后端类型 ${typeDecl[1]} 缺少说明类作用的中文 Javadoc`,
                        suggestion_zh: "在类、接口、枚举或记录声明前补充中文 Javadoc，说明该类型承担的业务职责、边界和使用场景。",
                    });
                }
            }
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i] ?? "";
                const method = line.match(JAVA_METHOD_DECLARATION);
                if (!method)
                    continue;
                const returnType = method[1]?.trim() ?? "";
                const methodName = method[2] ?? "";
                const params = javaParams(method[3] ?? "");
                if (typeName && methodName === typeName)
                    continue;
                const doc = extractJavadocBefore(lines, i);
                const hasReturnDoc = returnType === "void" || /@return|返回|出参|响应|结果/.test(doc ?? "");
                const hasParamDoc = params.length === 0 || /@param|参数|入参|请求/.test(doc ?? "");
                if (!doc || !containsChinese(doc) || !hasParamDoc || !hasReturnDoc) {
                    findings.push({
                        category: "missing_method_doc",
                        severity: "hard_fail",
                        file,
                        evidence: `后端方法 ${methodName} 缺少说明方法、入参或出参的中文 Javadoc`,
                        suggestion_zh: "每个后端方法必须有中文 Javadoc：说明方法作用；有入参时说明参数语义；有返回值时说明返回对象或结果含义。",
                    });
                }
            }
            const importantLines = lines
                .map((line, index) => ({ line, index }))
                .filter(({ line }) => IMPORTANT_BACKEND_LINE.test(line));
            const missingImportantComment = importantLines.some(({ index }) => !hasNearbyChineseLineComment(lines, index));
            if (importantLines.length > 0 && missingImportantComment) {
                findings.push({
                    category: "missing_important_line_comment",
                    severity: "hard_fail",
                    file,
                    evidence: "后端关键写入、状态变更、外部调用或账号联动行缺少中文行注释",
                    suggestion_zh: "在关键业务行附近补充中文行注释，说明为什么需要该写入、状态变更、外部调用或联动操作，以及失败/回滚边界。",
                });
            }
        }
        for (const { pattern, name_zh } of SENSITIVE_PATTERNS) {
            if (pattern.test(content)) {
                findings.push({
                    category: "sensitive_log_leak",
                    severity: "hard_fail",
                    file,
                    evidence: `日志或输出可能包含敏感信息: ${name_zh}`,
                    suggestion_zh: `对 ${name_zh} 字段脱敏后再输出（如仅显示末四位或哈希值）。`,
                });
            }
        }
        for (const { pattern, name_zh } of LOG_OUTPUT_SENSITIVE_PATTERNS) {
            if (pattern.test(content)) {
                findings.push({
                    category: "sensitive_log_leak",
                    severity: "hard_fail",
                    file,
                    evidence: `日志输出包含敏感信息: ${name_zh}`,
                    suggestion_zh: `禁止在日志中输出敏感字段值，仅记录脱敏值或哈希。`,
                });
            }
        }
    }
    return findings;
}
/* ── Review: 缺失日志检测 ─────────────────────── */
export function reviewMissingLogs(fileContents) {
    const findings = [];
    for (const [file, content] of Object.entries(fileContents)) {
        if (/\.(?:test|spec)\./.test(file))
            continue;
        const lowered = file.toLowerCase();
        const hasLog = /(?:log\.(info|warn|error|debug)|logger\.(info|warn|error|debug|log)|LOG\.(INFO|WARN|ERROR)|console\.(log|error|warn))\s*\(/i.test(content);
        const hasProjectLog = /(?:log\.(info|warn|error|debug)|logger\.(info|warn|error|debug|log)|LOG\.(INFO|WARN|ERROR))\s*\(/i.test(content);
        const logStatements = content.match(/(?:log\.|logger\.|LOG\.|console\.)\s*(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\([^)]*\)/gi) ?? [];
        for (const stmt of logStatements) {
            const firstLiteral = stmt.match(/["'`]([^"'`]+)["'`]/)?.[1] ?? "";
            if (firstLiteral && /[A-Za-z\u4e00-\u9fff]/.test(firstLiteral) && !containsChinese(firstLiteral)) {
                findings.push({
                    category: "non_chinese_log",
                    severity: "hard_fail",
                    file,
                    evidence: `用户项目日志必须使用中文: ${firstLiteral.slice(0, 80)}`,
                    suggestion_zh: "将日志事件名和说明改为中文，并保留业务对象 ID、操作人、前后状态等可定位字段。",
                });
            }
        }
        const backendWrite = isBackendImplementationFile(file)
            && (BUSINESS_WRITE_ENDPOINT.test(content) || BUSINESS_WRITE_CALL.test(content) || TRANSACTIONAL_OR_CASCADE_KEYWORDS.test(content));
        if (backendWrite && !hasProjectLog) {
            findings.push({
                category: "missing_log_business_write",
                severity: "hard_fail",
                file,
                evidence: "后端业务写操作缺少结构化业务日志",
                suggestion_zh: "Controller/Service 的新增、更新、删除、状态流转、事务双写、级联校验等操作必须记录业务日志，至少包含业务对象 ID、操作人、事件名和结果。",
            });
        }
        if (backendWrite && hasProjectLog) {
            const logStmts = content.match(/(?:log\.|logger\.|LOG\.)\s*(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\([^)]*\)/gi) ?? [];
            const hasLocatableField = logStmts.some((stmt) => /\w+(?:Id|_id|ID|No|Number|Code)\b/i.test(stmt)
                || /\bid\s*[=：:]/i.test(stmt)
                || /(?:before|after|prev|old|new|from|to)\s*[=：:]/i.test(stmt)
                || /(?:操作人|操作者|operator|userId|user_id|adminId|accountId)\b/i.test(stmt)
                || /(?:resourceId|target|resource)\s*[=：:]/i.test(stmt));
            if (!hasLocatableField) {
                findings.push({
                    category: "unlocatable_log",
                    severity: "hard_fail",
                    file,
                    evidence: "后端业务写操作日志缺少可定位字段（业务对象ID、操作人、前后状态或目标资源）",
                    suggestion_zh: "日志必须包含业务对象 ID、操作人、前后状态或目标资源，不能只写“成功/完成/已更新”。",
                });
            }
        }
        const frontendWrite = isFrontendImplementationFile(file)
            && (FRONTEND_MUTATION_CALL.test(content) || (FRONTEND_STATE_MUTATION.test(content) && /(?:submit|save|delete|status|payment|permission|role|auth|upload|导入|导出|审批|支付|权限|状态)/i.test(`${file} ${content}`)));
        if (frontendWrite && !hasProjectLog) {
            findings.push({
                category: "missing_log_business_write",
                severity: "hard_fail",
                file,
                evidence: "前端业务写操作或关键交互缺少可观测记录",
                suggestion_zh: "表单提交、状态变更、权限动作、支付/敏感操作、上传导入等前端关键交互必须记录可定位日志、埋点或错误上报，至少包含事件名、业务对象 ID 和结果。",
            });
        }
        if (frontendWrite && hasProjectLog) {
            const logStmts = content.match(/(?:log\.|logger\.|LOG\.)\s*(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\([^)]*\)/gi) ?? [];
            const hasLocatableField = logStmts.some((stmt) => /\w+(?:Id|_id|ID|No|Number|Code)\b/i.test(stmt)
                || /\bid\s*[=：:]/i.test(stmt)
                || /(?:before|after|prev|old|new|from|to)\s*[=：:]/i.test(stmt)
                || /(?:operator|userId|user_id|accountId|resourceId|target|resource)\b/i.test(stmt));
            if (!hasLocatableField) {
                findings.push({
                    category: "unlocatable_log",
                    severity: "hard_fail",
                    file,
                    evidence: "前端关键交互日志缺少可定位字段（事件对象ID、操作人、前后状态或目标资源）",
                    suggestion_zh: "前端日志/埋点必须能定位到业务对象和操作结果，不能只写“提交成功/保存成功”。",
                });
            }
        }
        if (CRITICAL_LOG_KEYWORDS.test(`${file} ${content}`)) {
            const hasWriteOp = /\.(?:save|insert|update|delete|create|refund|charge|pay)\s*\(/i.test(content);
            if (hasWriteOp && !/(?:log\.|logger\.|LOG\.)/i.test(content)) {
                findings.push({
                    category: "missing_log_critical",
                    severity: "hard_fail",
                    file,
                    evidence: "支付/金额相关写操作缺少业务日志",
                    suggestion_zh: "补充包含对象 ID、操作人、金额前后值和操作结果的结构化日志。",
                });
            }
            // 有日志但不可定位 — 支付/金额写操作的日志必须含对象ID或前后状态
            // 事件词（退款/支付/changed/updated）不是可定位字段
            if (hasWriteOp && /(?:log\.|logger\.|LOG\.)/i.test(content)) {
                const logStmts = content.match(/(?:log\.|logger\.|LOG\.|console\.)\s*(?:info|warn|error|debug|log|INFO|WARN|ERROR|DEBUG)\s*\([^)]*\)/gi) ?? [];
                const hasLocatableField = logStmts.some((stmt) => /\w+(?:Id|_id|ID|No|Number|Code)\b/i.test(stmt)
                    || /\bid\s*[=：:]/i.test(stmt)
                    || /(?:before|after|prev|old|new|from|to)\s*[=：:]/i.test(stmt)
                    || /(?:操作人|操作者|operator|userId|user_id|adminId|accountId)\b/i.test(stmt)
                    || /(?:resourceId|target|resource)\s*[=：:]/i.test(stmt));
                if (!hasLocatableField) {
                    findings.push({
                        category: "unlocatable_log",
                        severity: "hard_fail",
                        file,
                        evidence: "支付/金额写操作日志缺少可定位字段（对象ID、前后状态、操作人/目标资源）",
                        suggestion_zh: "日志必须包含对象 ID、前后状态或操作人+目标资源，事件词不是可定位字段。",
                    });
                }
            }
        }
        if (SECURITY_LOG_KEYWORDS.test(`${file} ${content}`)) {
            const hasPermissionCheck = /(?:permission|auth|role|check|deny|reject|forbidden|authorize)/i.test(content);
            if (hasPermissionCheck && !/(?:log\.|logger\.|LOG\.)/i.test(content)) {
                findings.push({
                    category: "missing_log_critical",
                    severity: "hard_fail",
                    file,
                    evidence: "权限校验逻辑缺少安全日志",
                    suggestion_zh: "补充包含操作人、目标资源和拒绝原因的安全日志。",
                });
            }
        }
        const catchBlocks = content.match(/catch\s*\([^)]*\)\s*\{[^}]*\}/g) ?? [];
        for (const block of catchBlocks) {
            const hasLogInBlock = /(?:log\.|logger\.|LOG\.|console\.(error|warn))/i.test(block);
            const onlyReturnOrRethrow = /^(?:\s*(?:return\s+(?:false|null|undefined|""|-1|0|\[\])|;?\s*\})+\s*$)/i.test(block.replace(/\s+/g, " ").trim());
            if (!hasLogInBlock && (onlyReturnOrRethrow || /return\s+(?:false|null|""|-1|0)/i.test(block))) {
                findings.push({
                    category: "catch_swallow",
                    severity: "hard_fail",
                    file,
                    evidence: `catch 块吞异常且无日志: ${block.slice(0, 80).replace(/\n/g, " ")}`,
                    suggestion_zh: "至少记录异常类型、消息和影响上下文，不得静默忽略。",
                });
            }
        }
        if (STATE_CHANGE_KEYWORDS.test(`${file} ${content}`)) {
            const hasStateUpdate = /(?:status|state)\s*(?:=|\.set|\.update|transition|change)\s*/i.test(content);
            if (hasStateUpdate && !/(?:log\.|logger\.|LOG\.)/i.test(content)) {
                findings.push({
                    category: "missing_log_state_change",
                    severity: "warning",
                    file,
                    evidence: "状态变更逻辑缺少流转日志",
                    suggestion_zh: "补充状态流转日志，记录变更前后状态和触发原因。",
                });
            }
        }
        if (EXTERNAL_CALL_KEYWORDS.test(`${file} ${content}`)) {
            const hasExternalCall = /(?:fetch|axios|RestTemplate|WebClient|Feign|http\.request|rpc|grpc)/i.test(content);
            if (hasExternalCall && !/(?:log\.|logger\.|LOG\.)/i.test(content)) {
                findings.push({
                    category: "missing_log_external",
                    severity: "warning",
                    file,
                    evidence: "外部调用缺少失败/超时日志",
                    suggestion_zh: "补充外部调用失败日志，含目标、响应码和重试信息。",
                });
            }
        }
        if (MIGRATION_KEYWORDS.test(`${file} ${content}`)) {
            const hasDataModification = /(?:UPDATE|INSERT|DELETE|update\(|save\(|delete\(|remove\()/i.test(content);
            if (hasDataModification && !/(?:log\.|logger\.|LOG\.|audit|审计)/i.test(content)) {
                findings.push({
                    category: "missing_audit_log",
                    severity: "hard_fail",
                    file,
                    evidence: "数据修复/迁移脚本缺少审计日志",
                    suggestion_zh: "补充审计日志，记录修复前值、后值和影响行数。",
                });
            }
        }
        if (hasLog && /console\.(log|debug|info)\s*\(/i.test(content) && !/\.(?:test|spec)\./.test(file)) {
            const isFrontendProd = /\.(?:vue|tsx|jsx|svelte)\b/.test(lowered);
            findings.push({
                category: "raw_console_log",
                severity: isFrontendProd ? "hard_fail" : "warning",
                file,
                evidence: "生产代码使用裸 console.log/debug/info",
                suggestion_zh: isFrontendProd
                    ? "前端生产代码禁止裸 console 输出，使用项目封装 logger 或移除。"
                    : "使用项目 logger 替代 console.log，确保日志可追踪和可过滤。",
            });
        }
        if (hasLog) {
            const logLines = content.match(/(?:log\.info|logger\.info|LOG\.INFO|console\.log)\s*\(\s*["'](?:error|failed|success|done|ok|ok|完成|失败|错误)["']/gi) ?? [];
            for (const _line of logLines) {
                findings.push({
                    category: "unlocatable_log",
                    severity: "warning",
                    file,
                    evidence: "日志仅含通用状态词，无业务对象标识",
                    suggestion_zh: "日志必须包含事件名、业务对象 ID 和操作上下文，确保可定位。",
                });
            }
        }
        const loopLogPattern = /(?:for|while|forEach|map|flatMap|reduce)\s*\([^)]*\)\s*\{[^}]*(?:log\.|logger\.|console\.)/i;
        if (loopLogPattern.test(content) && !/(?:batch|批量|each|every|per.?item)/i.test(content)) {
            findings.push({
                category: "noisy_log",
                severity: "advisory",
                file,
                evidence: "循环内可能产生刷屏日志",
                suggestion_zh: "在循环内使用 DEBUG 级别，或汇总后以单条日志输出。",
            });
        }
    }
    return findings;
}
/* ── Review: 注释质量检测 ─────────────────────── */
export function reviewCommentQuality(fileContents) {
    const findings = [];
    for (const [file, content] of Object.entries(fileContents)) {
        if (/\.(?:test|spec)\./.test(file))
            continue;
        if (COMPLEX_RULE_KEYWORDS.test(`${file} ${content}`)) {
            const hasFunction = /(?:function\s+\w+|(?:const|let)\s+\w+\s*=\s*(?:async\s+)?\(|(?:public|private|protected)\s+(?:\w+\s+)+\w+\s*\(|def\s+\w+\s*\(|class\s+\w+)/i.test(content);
            const hasComment = /(?:\/\/|\/\*|\*|<!--)/.test(content);
            if (hasFunction && !hasComment) {
                findings.push({
                    category: "missing_comment_complex",
                    severity: "warning",
                    file,
                    evidence: "复杂业务规则缺少注释",
                    suggestion_zh: "添加注释说明设计意图（为什么），不得只复述代码行为。",
                });
            }
        }
        const backendComplexRule = isBackendImplementationFile(file)
            && TRANSACTIONAL_OR_CASCADE_KEYWORDS.test(content)
            && /(?:class\s+\w+|(?:public|private|protected)\s+(?:\w+\s+)+\w+\s*\()/i.test(content);
        if (backendComplexRule && !/(?:\/\/|\/\*|\*|@Schema|@ApiModelProperty)/.test(content)) {
            findings.push({
                category: "missing_comment_complex",
                severity: "warning",
                file,
                evidence: "后端复杂业务规则缺少解释性注释",
                suggestion_zh: "级联删除校验、状态转换、事务双写、账号联动、密码重置等规则必须说明业务原因、边界和不变量。",
            });
        }
        const frontendComplexRule = isFrontendImplementationFile(file)
            && /(?:权限|状态|审批|支付|金额|表单联动|级联|缓存一致|乐观更新|回滚|debounce|throttle|optimistic|rollback|permission|status|workflow|payment|validation)/i.test(`${file} ${content}`)
            && /(?:function\s+\w+|const\s+\w+\s*=\s*(?:async\s*)?\(|watch\(|useEffect\(|computed\()/i.test(content);
        if (frontendComplexRule && !/(?:\/\/|\/\*|\*|<!--)/.test(content)) {
            findings.push({
                category: "missing_comment_complex",
                severity: "warning",
                file,
                evidence: "前端复杂交互规则缺少解释性注释",
                suggestion_zh: "权限控制、状态流转、表单联动、乐观更新/回滚、支付等复杂交互必须说明业务原因和边界。",
            });
        }
        if (isBackendDataContractFile(file) && !/\benum\s+\w+/i.test(content)) {
            const lines = content.split(/\r?\n/);
            let fieldCount = 0;
            let documentedFieldCount = 0;
            for (let i = 0; i < lines.length; i++) {
                const line = lines[i] ?? "";
                if (!/\bprivate\s+(?!static\b)(?:final\s+)?[\w<>?,\s]+\s+\w+\s*;/i.test(line))
                    continue;
                fieldCount++;
                const window = lines.slice(Math.max(0, i - 4), i).join("\n");
                if (FIELD_DOC_ANNOTATION.test(window))
                    documentedFieldCount++;
            }
            if (fieldCount >= 3 && documentedFieldCount < fieldCount) {
                findings.push({
                    category: "missing_domain_field_comment",
                    severity: "hard_fail",
                    file,
                    evidence: `领域/DTO 字段说明不完整: ${documentedFieldCount}/${fieldCount} 个字段具备说明`,
                    suggestion_zh: "Entity、Request、Response、VO 等用户项目契约类的每个业务字段都要有中文含义说明或 OpenAPI/Schema 注解，避免后续 AI 开发误解字段语义。",
                });
            }
        }
        const commentMatches = content.matchAll(/\/\/\s*(.+)|\/\*\*?\s*([\s\S]*?)\s*\*\//g);
        for (const match of commentMatches) {
            const commentText = (match[1] ?? match[2] ?? "").trim();
            if (!commentText)
                continue;
            if (/[A-Za-z\u4e00-\u9fff]/.test(commentText) && !containsChinese(commentText)) {
                findings.push({
                    category: "non_chinese_comment",
                    severity: "hard_fail",
                    file,
                    evidence: `用户项目注释必须使用中文: ${commentText.slice(0, 80)}`,
                    suggestion_zh: "将用户项目注释改为中文；保留必要的英文代码标识符，但业务说明、方法说明和风险说明必须中文表达。",
                });
            }
            const lineStart = match.index ?? 0;
            const afterComment = content.slice(lineStart + match[0].length, lineStart + match[0].length + 200);
            const nextCodeLine = afterComment.split("\n").find((l) => l.trim().length > 0 && !l.trim().startsWith("//") && !l.trim().startsWith("*"));
            if (nextCodeLine) {
                const codeStripped = nextCodeLine.replace(/[{}()[\];,.]/g, "").trim();
                const commentStripped = commentText.replace(/[{}()[\];,.]/g, "").trim();
                const codeLower = codeStripped.toLowerCase();
                const commentLower = commentStripped.toLowerCase();
                if (codeLower.length > 10 && commentLower.length > 10) {
                    const codeWords = new Set(codeLower.split(/\s+/));
                    const commentWords = new Set(commentLower.split(/\s+/));
                    let overlap = 0;
                    const total = Math.max(codeWords.size, commentWords.size);
                    for (const w of codeWords) {
                        if (commentWords.has(w) && w.length > 3)
                            overlap++;
                    }
                    if (total > 0 && overlap / total > 0.7) {
                        findings.push({
                            category: "verbose_comment",
                            severity: "advisory",
                            file,
                            evidence: `注释仅复述代码: "${commentText.slice(0, 60)}"`,
                            suggestion_zh: "删除废话注释；如需保留，改为说明设计意图或业务背景。",
                        });
                    }
                }
            }
        }
    }
    return findings;
}
/* ── Review: 项目 logger 使用检查 ─────────────── */
export function reviewLoggerUsage(fileContents, loggerInfo) {
    const findings = [];
    if (loggerInfo.type === "frontend_project" || loggerInfo.type === "none_detected")
        return findings;
    for (const [file, content] of Object.entries(fileContents)) {
        if (/\.(?:test|spec|d\.ts|\.types\.ts)\./.test(file))
            continue;
        const hasAnyLog = /(?:log\.|logger\.|LOG\.|console\.)/i.test(content);
        if (!hasAnyLog)
            continue;
        const usesProjectLogger = new RegExp(loggerInfo.call_pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\./g, "\\."), "i").test(content);
        const usesConsoleDirectly = /console\.(log|debug|info|warn|error)\s*\(/i.test(content);
        if (usesConsoleDirectly && !usesProjectLogger && loggerInfo.confidence === "high") {
            findings.push({
                category: "wrong_logger",
                severity: "warning",
                file,
                evidence: `使用裸 console 而非项目 logger (${loggerInfo.type})`,
                suggestion_zh: `使用项目 ${loggerInfo.type} logger 确保日志可追踪和可过滤。`,
            });
        }
    }
    return findings;
}
/* ── 综合审查入口 ─────────────────────────────── */
export function reviewCodeObservability(fileContents, loggerInfo) {
    const findings = [];
    findings.push(...detectSensitiveLogs(fileContents));
    findings.push(...reviewMissingLogs(fileContents));
    findings.push(...reviewCommentQuality(fileContents));
    const resolvedLogger = loggerInfo ?? detectProjectLogger(fileContents);
    findings.push(...reviewLoggerUsage(fileContents, resolvedLogger));
    return findings;
}
/* ── 低风险跳过判断 ───────────────────────────── */
export function isLowRiskChange(intent, changedFiles = []) {
    const text = `${intent} ${changedFiles.join(" ")}`;
    if (/^(?:fix\s+)?(?:typo|错别字|拼写|文案|README|注释样式|格式化|lint|import\s*排序)/i.test(text.trim())) {
        return { low_risk: true, reason_zh: "纯文案或格式修改，跳过可维护性/可观测性检查" };
    }
    if (/^(?:get|set|is|has|can)\w*\s*[({]/i.test(text.trim()) && changedFiles.length <= 1) {
        return { low_risk: true, reason_zh: "简单 getter/setter，跳过可维护性/可观测性检查" };
    }
    const allStyle = changedFiles.length > 0 && changedFiles.every((f) => /\.(?:css|scss|less|styl|style)$/i.test(f));
    if (allStyle) {
        return { low_risk: true, reason_zh: "纯样式修改，跳过可维护性/可观测性检查" };
    }
    const allTest = changedFiles.length > 0 && changedFiles.every((f) => /\.(?:test|spec)\./i.test(f));
    if (allTest && !OBSERVABILITY_KEYWORDS.test(text)) {
        return { low_risk: true, reason_zh: "纯测试文件修改，跳过可维护性/可观测性检查" };
    }
    return { low_risk: false, reason_zh: "" };
}
/* ── 阻断判断 ─────────────────────────────────── */
export function hasBlockingObservabilityFindings(findings) {
    return findings.some((f) => f.severity === "hard_fail");
}
export function hasP1OrAboveFindings(findings) {
    return findings.some((f) => f.severity === "hard_fail" || f.severity === "warning");
}
/* ── sf_verify changed_files 校验 ─────────────── */
export function verifyChangedFilesObservability(input) {
    const filtered = {};
    for (const [file, content] of Object.entries(input.file_contents)) {
        if (/\.(?:test|spec|d\.ts|\.types\.ts|\.md|\.json|\.yaml|\.yml|\.lock|\.map)$/i.test(file))
            continue;
        if (input.changed_files.length > 0 && !input.changed_files.some((cf) => file.endsWith(cf) || cf.endsWith(file)))
            continue;
        filtered[file] = content;
    }
    if (Object.keys(filtered).length === 0) {
        return [];
    }
    const lowRisk = isLowRiskChange(input.intent, input.changed_files);
    if (lowRisk.low_risk) {
        return [{ category: "low_risk_skip", severity: "info", file: "", evidence: lowRisk.reason_zh, suggestion_zh: "" }];
    }
    return reviewCodeObservability(filtered);
}
/* ── sf_deliver 交付阻断 ──────────────────────── */
export function evaluateDeliveryBlock(findings) {
    const blocking = findings.filter((f) => f.severity === "hard_fail"
        || (f.severity === "warning" && f.category === "missing_comment_complex"));
    if (blocking.length > 0) {
        const reasons = blocking.map((f) => `[${f.category}] ${f.file}: ${f.evidence}`).join("\n");
        return {
            blocked: true,
            reason_zh: `代码可维护性/可观测性存在 ${blocking.length} 项阻断:\n${reasons}`,
            blocking_findings: blocking,
        };
    }
    return { blocked: false, reason_zh: "", blocking_findings: [] };
}
//# sourceMappingURL=code_maintainability_observability_contract.js.map