/**
 * Step A 冒烟 harness — 镜像 GoogleGenAiAdapter 中 P0 热路径分支，供集成测试断言。
 * 不启动真实 Gemini / MCP；V2 可选 env 触发真实 write_task。
 */
import {
  checkGoogleToolAuthority,
  formatGoogleAuthorityDeniedPayload,
} from "../googleAuthorityGuard.ts";
import {
  isGeminiModelNotFoundError,
  formatGeminiModelNotFoundMessage,
  classifyGeminiApiError,
} from "../googleGeminiApiErrors.ts";
import {
  buildFunctionResponseUserTurn,
  validateFunctionResponseAlignment,
  repairFunctionResponseUserTurn,
  type GeminiToolResult,
} from "../geminiChatHistory.ts";
import type { GeminiChatContent } from "../geminiChatMemory.ts";
import { GoogleRunHandle } from "../GoogleGenAiAdapter.ts";
import {
  guardWriteReportTaskId,
  resolveSessionPinnedTaskId,
  type SessionPinnedTaskIdResolution,
  type WriteReportTaskIdGuardResult,
} from "../geminiWriteReportTaskIdGuard.ts";
import { buildGoogleFailurePayloadFields } from "../../session/google-failure-detector.ts";
import type { RuntimeEvent } from "../../types/state.ts";

export type RuntimeAlertIngestPayload = {
  code: string;
  severity: "P0" | "P1" | "P2" | "P3";
  category:
    | "sdk_network"
    | "role_boundary"
    | "shell_tool"
    | "lifecycle"
    | "report_gate"
    | "wake_dispatch"
    | "persistence"
    | "panel_perf"
    | "concurrency_lock";
  title: string;
  message: string;
};

export type AuthoritySmokeResult = {
  settledStatus: "failed" | "finished";
  events: RuntimeEvent[];
  toolInvoked: boolean;
  sdkFailureDetail?: Record<string, unknown>;
  alerts: RuntimeAlertIngestPayload[];
};

/** 镜像 adapter executeGeminiToolCall 的 authority 分支（约 1229–1277 行）。 */
export async function runAuthorityToolSmoke(opts: {
  agentId: string;
  toolName: string;
  args?: Record<string, unknown>;
  invokeTool?: (
    toolName: string,
    args: Record<string, unknown>,
  ) => Promise<string>;
  onAlert?: (payload: RuntimeAlertIngestPayload) => void;
}): Promise<AuthoritySmokeResult> {
  const handle = new GoogleRunHandle({
    runId: "smoke-run",
    sessionId: "smoke-session",
    agentId: opts.agentId,
  });
  const events: RuntimeEvent[] = [];
  handle.onEvent((e) => events.push(e));

  const alerts: RuntimeAlertIngestPayload[] = [];
  const ingestAlert = (payload: RuntimeAlertIngestPayload) => {
    alerts.push(payload);
    opts.onAlert?.(payload);
  };

  let settledStatus: "failed" | "finished" = "finished";
  let toolInvoked = false;
  const failedToolNames = new Set<string>();
  const toolName = opts.toolName;
  const callId = `smoke-${toolName}-0`;
  const args = opts.args ?? {};

  handle.emit("sdk.tool_call", {
    raw: {
      name: toolName,
      args,
      status: "running",
      call_id: callId,
      id: callId,
    },
  });

  const authority = checkGoogleToolAuthority({
    agentId: opts.agentId,
    toolName,
  });

  if (!authority.allowed) {
    const denialPayload = authority.error!;
    const denialText = formatGoogleAuthorityDeniedPayload(denialPayload);
    failedToolNames.add(toolName);
    settledStatus = "failed";

    handle.emit("sdk.tool_result", {
      raw: {
        name: toolName,
        args,
        status: "failed",
        call_id: callId,
        id: callId,
        output: denialText,
      },
    });
    handle.emit("sdk.status", {
      raw: {
        status: "failed",
        error: denialPayload.message,
        code: "AUTHORITY_DENIED",
        tool: toolName,
      },
    });
    ingestAlert({
      code: "AUTHORITY_DENIED",
      severity: "P1",
      category: "role_boundary",
      title: "Google 工具权限拒绝",
      message: denialPayload.message,
    });
  } else if (opts.invokeTool) {
    toolInvoked = true;
    try {
      const output = await opts.invokeTool(toolName, args);
      handle.emit("sdk.tool_result", {
        raw: {
          name: toolName,
          args,
          status: "success",
          call_id: callId,
          id: callId,
          output,
        },
      });
    } catch (err) {
      settledStatus = "failed";
      failedToolNames.add(toolName);
      const errMsg = err instanceof Error ? err.message : String(err);
      handle.emit("sdk.tool_result", {
        raw: {
          name: toolName,
          args,
          status: "failed",
          call_id: callId,
          id: callId,
          output: errMsg,
        },
      });
    }
  } else {
    handle.emit("sdk.tool_result", {
      raw: {
        name: toolName,
        args,
        status: "success",
        call_id: callId,
        id: callId,
        output: JSON.stringify({ ok: true, smoke: true }),
      },
    });
  }

  const failureCtx =
    settledStatus === "failed"
      ? {
          error_message: authority.allowed
            ? "tool failed"
            : authority.error!.message,
          failed_tool_names: [...failedToolNames],
          raw: authority.allowed
            ? { status: "error" }
            : {
                status: "failed",
                code: "AUTHORITY_DENIED",
                tool: toolName,
              },
        }
      : undefined;

  handle.settle(settledStatus, 1, failureCtx);
  const settled = await handle.whenSettled();
  const sdkFailureDetail = (settled as { sdk_failure_detail?: Record<string, unknown> })
    .sdk_failure_detail;

  return {
    settledStatus,
    events,
    toolInvoked,
    sdkFailureDetail,
    alerts,
  };
}

export type GeminiRetryDecision =
  | { action: "no_retry_break"; reason: "MODEL_NOT_FOUND" }
  | { action: "retry"; nextRetryCount: number }
  | { action: "no_retry_throw" };

/** 镜像 adapter generateContentStream catch 重试决策（约 1011–1052 行）。 */
export function decideGeminiRetryOnError(
  err: unknown,
  retryCount: number,
  maxRetries: number,
  modelId = "gemini-smoke-model",
): GeminiRetryDecision {
  if (isGeminiModelNotFoundError(err)) {
    void formatGeminiModelNotFoundMessage(err, modelId);
    return { action: "no_retry_break", reason: "MODEL_NOT_FOUND" };
  }

  const errStr = String(err);
  const is503 =
    errStr.includes("503") || errStr.toLowerCase().includes("unavailable");
  const is429 =
    errStr.includes("429") ||
    errStr.toLowerCase().includes("exhausted") ||
    errStr.toLowerCase().includes("rate limit");

  if ((is503 || is429) && retryCount < maxRetries) {
    return { action: "retry", nextRetryCount: retryCount + 1 };
  }

  return { action: "no_retry_throw" };
}

export type FunctionResponseRoundResult = {
  strikes: number;
  broken: boolean;
  chatHistoryPushed: boolean;
  turn: GeminiChatContent | null;
  align: { ok: boolean; callCount: number; responseCount: number };
  alert?: RuntimeAlertIngestPayload;
  statusCode?: "FUNCTION_RESPONSE_MISALIGNED";
};

/** 镜像 adapter functionResponse 对齐 / repair / 熔断（约 1457–1521 行）。 */
export function processFunctionResponseRound(opts: {
  modelParts: readonly unknown[];
  toolResults: readonly GeminiToolResult[];
  strikes: number;
  round?: number;
  /** 测试用：跳过 build，模拟 history 中已存在的错位 user turn */
  initialUserTurn?: GeminiChatContent;
}): FunctionResponseRoundResult {
  let frAlignMismatchStrikes = opts.strikes;
  const round = opts.round ?? 1;

  let frTurn =
    opts.initialUserTurn ??
    buildFunctionResponseUserTurn(opts.modelParts, opts.toolResults);
  let frAlign = validateFunctionResponseAlignment(opts.modelParts, frTurn);
  let frAlignmentBroken = false;
  let alert: RuntimeAlertIngestPayload | undefined;

  if (!frAlign.ok) {
    frAlignMismatchStrikes++;
    if (frAlignMismatchStrikes === 1) {
      const repaired = repairFunctionResponseUserTurn(
        opts.modelParts,
        opts.toolResults,
      );
      const repairedAlign = validateFunctionResponseAlignment(
        opts.modelParts,
        repaired,
      );
      if (repairedAlign.ok) {
        frTurn = repaired;
        frAlign = repairedAlign;
      } else {
        frAlignMismatchStrikes = 2;
      }
    }

    if (frAlignMismatchStrikes >= 2) {
      frAlignmentBroken = true;
      const errMsg = `FunctionResponse alignment 熔断：calls=${frAlign.callCount} responses=${frAlign.responseCount} round=${round}`;
      alert = {
        code: "FUNCTION_RESPONSE_MISALIGNED",
        severity: "P0",
        category: "shell_tool",
        title: "Gemini FunctionResponse 错位",
        message: errMsg,
      };
    }
  }

  const chatHistoryPushed = !frAlignmentBroken;

  return {
    strikes: frAlignMismatchStrikes,
    broken: frAlignmentBroken,
    chatHistoryPushed,
    turn: chatHistoryPushed ? frTurn : null,
    align: frAlign,
    alert,
    statusCode: frAlignmentBroken ? "FUNCTION_RESPONSE_MISALIGNED" : undefined,
  };
}

/** 镜像 GoogleGenAiAdapter write_report task_id guard（约 747–756、1269–1344 行）。 */
export type WriteReportTaskIdGuardSmokeResult = {
  pinnedResolution: SessionPinnedTaskIdResolution;
  guardResult: WriteReportTaskIdGuardResult;
  /** MCP invoke 前实际使用的 args（guard repair/pass 之后） */
  effectiveArgs: Record<string, unknown>;
  wouldInvokeMcp: boolean;
};

export function runWriteReportTaskIdGuardSmoke(opts: {
  sendSpec: {
    pinnedTaskId?: string | null;
    taskFilepath?: string | null;
    frontmatterTaskId?: string | null;
    promptText?: string | null;
  };
  writeReportArgs: Record<string, unknown>;
}): WriteReportTaskIdGuardSmokeResult {
  const pinnedResolution = resolveSessionPinnedTaskId({
    pinnedTaskId: opts.sendSpec.pinnedTaskId,
    taskFilepath: opts.sendSpec.taskFilepath,
    frontmatterTaskId: opts.sendSpec.frontmatterTaskId,
    promptText: opts.sendSpec.promptText,
  });
  const guardResult = guardWriteReportTaskId(
    opts.writeReportArgs,
    pinnedResolution.pinnedTaskId,
  );

  if (guardResult.action === "reject") {
    return {
      pinnedResolution,
      guardResult,
      effectiveArgs: opts.writeReportArgs,
      wouldInvokeMcp: false,
    };
  }

  return {
    pinnedResolution,
    guardResult,
    effectiveArgs: guardResult.args,
    wouldInvokeMcp: true,
  };
}

/** MODEL_NOT_FOUND 路径：构建 settle 用 sdk_failure_detail（Panel 可读）。 */
export function buildModelNotFoundFailureDetail(
  err: unknown,
  modelId: string,
  agentId: string,
  sessionId: string,
): Record<string, unknown> {
  const errMsg = formatGeminiModelNotFoundMessage(err, modelId);
  return buildGoogleFailurePayloadFields({
    status: "error",
    tool_call_count: 0,
    duration_ms: 1,
    raw: {
      status: "failed",
      error: errMsg,
      code: "MODEL_NOT_FOUND",
    },
    error_message: errMsg,
    failed_tool_names: ["gemini_model"],
    agent_id: agentId,
    session_id: sessionId,
  });
}

export { classifyGeminiApiError, isGeminiModelNotFoundError };
