package tools

// This file defines the four JSON write tools (P3-T4, plan §3.8):
// submit_vulnerability / update_vulnerability /
// transition_vulnerability / add_vulnerability_comment. They are
// appended to the registry in registry.go's Registry() and dispatched
// through vuln.go's dispatchVuln (single-endpoint tools, so
// vulnCallSingle → callEndpoint drives the POST/PUT + JSON body per
// each endpoint's Method/BodyMap).
//
// Contract source: the embedded swagger snapshot (P2-T2). BodyMap
// field names match the request DTO properties verbatim — P3-T6's
// gate re-checks them. Deliberate cuts (see per-tool comments):
//   - tags is not exposed anywhere (W0 拍板: 首版裁剪数组字段);
//   - submitter_user_id/submitted_at/sla_deadline are server-forced
//     for agents, so submit does not expose them;
//   - poc_text/verification_steps/has_poc_verification/submitted_at/
//     sla_deadline 403 the whole request for agents on update, so
//     update does not expose them.
func vulnWriteTools() []Tool {
	return []Tool{
		{
			Name: "submit_vulnerability",
			Description: "提交新漏洞。需要 vuln:submit_findings scope（无安全权限的用户/研发的 Key 无法配置该 scope，调用将被后端 403）。" +
				"必填：title、severity_level、status（通常取 active）、discoverer_employee_id；" +
				"后端可能通过 submission_required_fields 配置追加必填字段，缺失时服务端报错会给出字段名。" +
				"Agent 约束：提交人强制 = API Key 主人（submitter_user_id 不可指定，传了也会被忽略）；" +
				"submitted_at/sla_deadline 由服务端强制赋值，同样不可指定。" +
				"插图：先调 upload_vulnerability_attachment 拿到 attachment_id，再在 description 的 HTML 里用 " +
				"data-sc-attachment-id=\"<attachment_id>\" 引用。暂不支持 tags（首版裁剪）。" +
				"典型场景：扫描 agent 代报漏洞（发现人/修复人/责任人可按实指定）。",
			// 单端点 POST /vulnerabilities。必填对齐 swagger
			// createVulnerabilityRequest.required；submitter_user_id /
			// submitted_at / sla_deadline 服务端强制、tags 裁剪，均不暴露。
			InputSchema: objectSchema(vulnDetailFields(map[string]Property{
				"title":          strProp("漏洞标题（必填）"),
				"severity_level": {Type: "string", Description: "严重程度（必填）", Enum: []string{"low", "medium", "high", "critical"}},
				"status": {Type: "string", Description: "初始状态（必填，新提交通常为 active）",
					Enum: []string{"active", "retesting", "fixed", "false_positive", "risk_accepted"}},
				"discoverer_employee_id": intProp("发现人员工 ID（必填）"),
				"description":            strProp("漏洞描述（HTML；插图先上传拿 attachment_id，再用 data-sc-attachment-id 引用）"),
				"fixer_employee_id":      intProp("修复人员工 ID（建议提交时设置：后续 request_retest 要求操作者就是修复人）"),
				"batch_group_id":         intProp("批次组 ID"),
				"has_poc_verification":   boolProp("是否已有 POC 验证"),
				"poc_text":               strProp("POC 内容"),
				"verification_steps":     strProp("验证步骤"),
			}), "title", "severity_level", "status", "discoverer_employee_id"),
			Endpoints: []EndpointRef{
				{Method: "post", Path: "/api/v1/vulnerabilities", BodyMap: bodyMap(
					"title", "severity_level", "status", "description", "application",
					"vulnerability_type", "source", "discovery_channel", "discovery_phase",
					"network_env", "interface_path", "code_repository", "department_id",
					"dependency_name", "dependency_version", "affected_assets_count",
					"batch_group_id", "discoverer_employee_id", "fixer_employee_id",
					"owner_employee_id", "is_third_party", "has_poc_verification",
					"poc_text", "verification_steps")},
			},
		},
		{
			Name: "update_vulnerability",
			Description: "按 public_id 部分更新漏洞的普通字段：只传要改的字段（可空字段传 null 即清除）。需要 vuln:edit_details scope。" +
				"Agent 约束：poc_text、verification_steps、has_poc_verification、submitted_at、sla_deadline 不开放修改——" +
				"试图变更会被后端整体 403，故本工具不暴露这五个参数；状态变更请改用 transition_vulnerability。" +
				"插图同 submit_vulnerability（先上传再以 data-sc-attachment-id 引用）。暂不支持 tags（首版裁剪）。" +
				"典型场景：修正标题/描述/严重程度/责任人/修复人等普通信息。",
			// 单端点 PUT /vulnerabilities/{public_id}。updateVulnerabilityPayload
			// 无 status 字段（状态只能走 transition）；五个 agent 403 字段与
			// tags 不暴露。
			InputSchema: objectSchema(vulnDetailFields(map[string]Property{
				"public_id":      strProp("漏洞 Public ID（字符串，来自 search_vulnerabilities 结果，非数字 ID）"),
				"title":          strProp("漏洞标题"),
				"severity_level": {Type: "string", Description: "严重程度", Enum: []string{"low", "medium", "high", "critical"}},
				"description":    strProp("漏洞描述（HTML；插图先上传拿 attachment_id，再用 data-sc-attachment-id 引用）"),
			}), "public_id"),
			Endpoints: []EndpointRef{
				{Method: "put", Path: "/api/v1/vulnerabilities/{public_id}",
					ParamMap: paramMap("public_id"),
					BodyMap: bodyMap(
						"title", "severity_level", "description", "application",
						"vulnerability_type", "source", "discovery_channel", "discovery_phase",
						"network_env", "interface_path", "code_repository", "department_id",
						"dependency_name", "dependency_version", "affected_assets_count",
						"discoverer_employee_id", "fixer_employee_id", "owner_employee_id",
						"is_third_party")},
			},
		},
		{
			Name: "transition_vulnerability",
			Description: "按 public_id 流转漏洞状态，status 为目标状态，五选一：" +
				"retesting（=request_retest 提交修复待复测）：仅 active → retesting，前置约束——操作者必须是该漏洞的修复人" +
				"（流程上先把 fixer_employee_id 设为 Key 主人关联员工再调用），agent Key 还需 vuln:request_retest scope；" +
				"fixed（=mark_fixed 复测通过）：仅 retesting → fixed；" +
				"false_positive（=mark_false_positive 标记误报）/ risk_accepted（=mark_risk_accepted 风险接受）：active 或 retesting → 目标态；" +
				"active（=reopen 重新打开）：fixed/false_positive/risk_accepted → active。" +
				"mark_fixed/mark_false_positive/mark_risk_accepted/reopen 需要 vuln:close_findings scope。" +
				"所有流转均可经 reopen 逆转，message 会记入事件流。" +
				"典型场景：修复人提交复测、验证方确认修复、误报/风险接受结案、打回重开。",
			// 单端点 POST /vulnerabilities/{public_id}/transition。契约字段
			// 是 status（transitionStatusRequest），不是 action；五个目标态
			// 与五个动作名的对应关系写进描述。
			InputSchema: objectSchema(map[string]Property{
				"public_id": strProp("漏洞 Public ID（字符串，来自 search_vulnerabilities 结果，非数字 ID）"),
				"status": {Type: "string", Description: "目标状态（必填，五选一；动作语义见工具描述）",
					Enum: []string{"retesting", "fixed", "false_positive", "risk_accepted", "active"}},
				"message": strProp("流转说明（记入事件流，如修复说明/误报理由/风险接受依据）"),
			}, "public_id", "status"),
			Endpoints: []EndpointRef{
				{Method: "post", Path: "/api/v1/vulnerabilities/{public_id}/transition",
					ParamMap: paramMap("public_id"),
					BodyMap:  bodyMap("status", "message")},
			},
		},
		{
			Name: "add_vulnerability_comment",
			Description: "按 public_id 给漏洞添加评论（content 必填），评论与操作者记入事件流；agent 评论在事件流中归属 API Key 名称。" +
				"约束：操作者必须是该漏洞的提交人、修复人或持有安全角色——agent 实际可评论范围 = 自己提交的、或 Key 主人是修复人的漏洞。" +
				"典型场景：复测说明、误报理由、风险接受依据留证。",
			// 单端点 POST /vulnerabilities/{public_id}/comments，
			// addCommentRequest 仅 content 一个必填字段。
			InputSchema: objectSchema(map[string]Property{
				"public_id": strProp("漏洞 Public ID（字符串，来自 search_vulnerabilities 结果，非数字 ID）"),
				"content":   strProp("评论内容（必填）"),
			}, "public_id", "content"),
			Endpoints: []EndpointRef{
				{Method: "post", Path: "/api/v1/vulnerabilities/{public_id}/comments",
					ParamMap: paramMap("public_id"),
					BodyMap:  bodyMap("content")},
			},
		},
	}
}

// vulnDetailFields returns the editable detail fields shared by the
// submit and update tools (createVulnerabilityRequest and
// updateVulnerabilityPayload carry them under identical names and
// types), overlaid with the per-tool extras. extras wins on conflict.
func vulnDetailFields(extras map[string]Property) map[string]Property {
	fields := map[string]Property{
		"application":            strProp("所属应用"),
		"vulnerability_type":     strProp("漏洞类型（可选值用 list_vuln_lookup_options kind=types 查询）"),
		"source":                 strProp("漏洞来源"),
		"discovery_channel":      strProp("发现渠道"),
		"discovery_phase":        strProp("发现阶段"),
		"network_env":            strProp("网络环境"),
		"interface_path":         strProp("接口路径"),
		"code_repository":        strProp("代码仓库"),
		"department_id":          strProp("责任部门 ID（字符串）"),
		"dependency_name":        strProp("依赖名"),
		"dependency_version":     strProp("依赖版本"),
		"affected_assets_count":  intProp("影响资产数"),
		"discoverer_employee_id": intProp("发现人员工 ID"),
		"fixer_employee_id":      intProp("修复人员工 ID"),
		"owner_employee_id":      intProp("责任人员工 ID"),
		"is_third_party":         boolProp("是否第三方漏洞"),
	}
	for name, prop := range extras {
		fields[name] = prop
	}
	return fields
}
