import { redactJsonValue, redactText } from "./redaction";

export const commanderApprovalNotificationMaxChars = 200;
export const commanderApprovalClaudeQqProxyBasePath = "/api/microservices/claudeqq/proxy";
export const commanderApprovalClaudeQqPrimaryEndpoint = "/api/push/text";
export const commanderApprovalClaudeQqFallbackEndpoint = "/api/send/text";
export const commanderApprovalClaudeQqDefaultUserId = "645275593";
export const commanderApprovalClaudeQqTimeoutMs = 15_000;

export interface CommanderApprovalNotificationDraft {
  ok: true;
  channel: "claudeqq";
  language: "zh-CN";
  format: "plain-text";
  message: string;
  chars: number;
  maxChars: number;
  markdownAllowed: false;
  containsMarkdownSyntax: boolean;
  oneParagraph: boolean;
  redactionsApplied: number;
}

export interface CommanderApprovalNotificationPathUnavailable {
  ok: false;
  error: "notification-path-unavailable";
  degradedReason: "source-contract-dry-run-only";
  mutation: false;
  message: string;
  servicePath: string;
  fallbackServicePath: string;
  backendCoreProxyCommand: string;
  timeoutMs: number;
  forbiddenLocalPaths: string[];
}

const actionLabels: Record<string, string> = {
  "code-queue-backend-restart": "重启 Code Queue backend",
  "code-queue-backend-rebuild": "重建 Code Queue backend 容器",
  "code-queue-execution-plane-restart": "重启 Code Queue 执行面",
  "code-queue-task-interrupt": "interrupt 运行中任务",
  "code-queue-task-cancel": "cancel 运行中任务",
  "prod-runtime-mutation": "修改生产运行态",
};

function charLength(value: string): number {
  return Array.from(value).length;
}

function clampChars(value: string, maxChars: number): string {
  const chars = Array.from(value);
  if (chars.length <= maxChars) return value;
  if (maxChars <= 1) return "…";
  return `${chars.slice(0, maxChars - 1).join("")}…`;
}

function oneParagraph(value: string): string {
  return value.replace(/\s+/gu, " ").trim();
}

function removeMarkdownSyntax(value: string): string {
  return value
    .replace(/[`*_#[\]|]/gu, "")
    .replace(/^\s*[-+]\s+/gu, "")
    .trim();
}

function containsMarkdownSyntax(value: string): boolean {
  return /[`*_#[\]|]/u.test(value) || /\n/u.test(value);
}

function shellQuote(value: string): string {
  return `'${value.replace(/'/g, "'\\''")}'`;
}

export function commanderApprovalClaudeQqPayload(message: string, userId = commanderApprovalClaudeQqDefaultUserId): Record<string, unknown> {
  return {
    targetType: "private",
    userId,
    message,
  };
}

export function commanderApprovalClaudeQqProxyCommand(message: string): string {
  const payload = JSON.stringify(commanderApprovalClaudeQqPayload(message));
  return `bun scripts/cli.ts microservice proxy claudeqq ${commanderApprovalClaudeQqPrimaryEndpoint} --method POST --body-json ${shellQuote(payload)} --raw`;
}

export function buildCommanderApprovalNotificationDraft(input: { action: string; taskId?: string | null; reason: string }): CommanderApprovalNotificationDraft {
  const redacted = redactText(input.reason);
  const label = actionLabels[input.action] ?? removeMarkdownSyntax(input.action);
  const target = input.taskId === null || input.taskId === undefined || input.taskId.length === 0
    ? ""
    : `，目标 ${removeMarkdownSyntax(input.taskId)}`;
  const prefix = `请审批高风险 Code Queue 恢复：拟执行「${label}」${target}。原因：`;
  const suffix = "。未获明确同意前只做只读诊断和 issue 记录。";
  const fallbackReason = "需用户确认后才能执行";
  const reason = removeMarkdownSyntax(oneParagraph(redacted.text || fallbackReason)) || fallbackReason;
  const budget = Math.max(1, commanderApprovalNotificationMaxChars - charLength(prefix) - charLength(suffix));
  const message = `${prefix}${clampChars(reason, budget)}${suffix}`;
  const normalized = clampChars(oneParagraph(removeMarkdownSyntax(message)), commanderApprovalNotificationMaxChars);
  return {
    ok: true,
    channel: "claudeqq",
    language: "zh-CN",
    format: "plain-text",
    message: normalized,
    chars: charLength(normalized),
    maxChars: commanderApprovalNotificationMaxChars,
    markdownAllowed: false,
    containsMarkdownSyntax: containsMarkdownSyntax(normalized),
    oneParagraph: !normalized.includes("\n"),
    redactionsApplied: redacted.redactionsApplied,
  };
}

export function commanderApprovalNotificationPathUnavailable(message: string): CommanderApprovalNotificationPathUnavailable {
  return {
    ok: false,
    error: "notification-path-unavailable",
    degradedReason: "source-contract-dry-run-only",
    mutation: false,
    message: "host-codex-commander 当前阶段只生成审批通知草案；正式发送必须在授权后走 backend-core microservice proxy，不能回退到本机 ClaudeQQ skill/powershell。",
    servicePath: `${commanderApprovalClaudeQqProxyBasePath}${commanderApprovalClaudeQqPrimaryEndpoint}`,
    fallbackServicePath: `${commanderApprovalClaudeQqProxyBasePath}${commanderApprovalClaudeQqFallbackEndpoint}`,
    backendCoreProxyCommand: commanderApprovalClaudeQqProxyCommand(message),
    timeoutMs: commanderApprovalClaudeQqTimeoutMs,
    forbiddenLocalPaths: [
      "/root/.agents/skills/claudeqq",
      "powershell.exe",
      "local ClaudeQQ skill server",
      "localhost:9082",
    ],
  };
}

export function commanderApprovalProxyFailureSummary(response: unknown, endpoint = commanderApprovalClaudeQqPrimaryEndpoint): Record<string, unknown> {
  const redacted = redactJsonValue(response) as unknown;
  const record = typeof redacted === "object" && redacted !== null && !Array.isArray(redacted) ? redacted as Record<string, unknown> : {};
  const status = typeof record.status === "number" ? record.status : null;
  const message = typeof record.error === "string"
    ? record.error
    : typeof record.message === "string"
      ? record.message
      : typeof record.stderrTail === "string"
        ? record.stderrTail.slice(-500)
        : "ClaudeQQ microservice proxy request failed";
  return {
    ok: false,
    error: "notification-proxy-failed",
    degradedReason: "microservice-proxy-failed",
    runnerDisposition: "infra-blocked",
    endpoint,
    servicePath: `${commanderApprovalClaudeQqProxyBasePath}${endpoint}`,
    status,
    message: redactText(message).text,
    response: redacted,
    rollback: {
      issueUpdateRolledBack: false,
      policy: "ClaudeQQ notification is best-effort; record the failure in the related issue or commander brief instead of rolling back completed GitHub issue updates.",
    },
  };
}
