import {
  isWebchatSessionMutationBlockedError,
  syncGatewaySessionSettings,
  type GatewayClient,
} from "@/lib/gateway/GatewayClient";
import {
  buildAgentInstruction,
  isMetaMarkdown,
  parseMetaMarkdown,
} from "@/lib/text/message-extract";
import type { AgentState } from "@/features/agents/state/store";
import { randomUUID } from "@/lib/uuid";
import type { TranscriptAppendMeta } from "@/features/agents/state/transcript";

type SendDispatchAction =
  | { type: "updateAgent"; agentId: string; patch: Partial<AgentState> }
  | { type: "appendOutput"; agentId: string; line: string; transcript?: TranscriptAppendMeta };

type SendDispatch = (action: SendDispatchAction) => void;

type GatewayClientLike = {
  call: (method: string, params: unknown) => Promise<unknown>;
};

const resolveLatestTranscriptTimestampMs = (agent: AgentState): number | null => {
  const entries = agent.transcriptEntries;
  let latest: number | null = null;
  if (Array.isArray(entries)) {
    for (const entry of entries) {
      const ts = entry?.timestampMs;
      if (typeof ts !== "number" || !Number.isFinite(ts)) continue;
      latest = latest === null ? ts : Math.max(latest, ts);
    }
  }
  if (latest !== null) return latest;
  const lines = agent.outputLines;
  for (const line of lines) {
    if (!isMetaMarkdown(line)) continue;
    const parsed = parseMetaMarkdown(line);
    const ts = parsed?.timestamp;
    if (typeof ts !== "number" || !Number.isFinite(ts)) continue;
    latest = latest === null ? ts : Math.max(latest, ts);
  }
  return latest;
};

const resolveChatSendCompletionMode = (
  payload: unknown,
  optimisticRunId: string
): "streaming-expected" | "terminal-immediate" => {
  if (!payload || typeof payload !== "object") {
    return "terminal-immediate";
  }
  const value = payload as { status?: unknown; runId?: unknown };
  const status = typeof value.status === "string" ? value.status.trim().toLowerCase() : "";
  const runId = typeof value.runId === "string" ? value.runId.trim() : "";
  if ((status === "started" || status === "in_flight") && runId === optimisticRunId) {
    return "streaming-expected";
  }
  return "terminal-immediate";
};

export async function sendChatMessageViaStudio(params: {
  client: GatewayClientLike;
  dispatch: SendDispatch;
  getAgent: (agentId: string) => AgentState | null;
  agentId: string;
  sessionKey: string;
  message: string;
  clearRunTracking?: (runId: string) => void;
  echoUserMessage?: boolean;
  now?: () => number;
  generateRunId?: () => string;
}): Promise<void> {
  const trimmed = params.message.trim();
  if (!trimmed) return;
  const echoUserMessage = params.echoUserMessage !== false;

  const generateRunId = params.generateRunId ?? (() => randomUUID());
  const now = params.now ?? (() => Date.now());

  const agentId = params.agentId;
  const runId = generateRunId();

  params.clearRunTracking?.(runId);

  const agent = params.getAgent(agentId);
  if (!agent) {
    params.dispatch({
      type: "appendOutput",
      agentId,
      line: "Error: Agent not found.",
    });
    return;
  }

  const isResetCommand = /^\/(reset|new)(\s|$)/i.test(trimmed);
  if (isResetCommand) {
    params.dispatch({
      type: "updateAgent",
      agentId,
      patch: {
        outputLines: [],
        streamText: null,
        thinkingTrace: null,
        lastResult: null,
        sessionEpoch: (agent.sessionEpoch ?? 0) + 1,
        transcriptEntries: [],
        lastHistoryRequestRevision: null,
        lastAppliedHistoryRequestId: null,
      },
    });
  }

  const userTimestamp = now();
  const latestTranscriptTimestamp = resolveLatestTranscriptTimestampMs(agent);
  const optimisticUserOrderTimestamp =
    typeof latestTranscriptTimestamp === "number"
      ? Math.max(userTimestamp, latestTranscriptTimestamp + 1)
      : userTimestamp;
  params.dispatch({
    type: "updateAgent",
    agentId,
    patch: {
      status: "running",
      runId,
      runStartedAt: userTimestamp,
      streamText: "",
      thinkingTrace: null,
      draft: "",
      ...(echoUserMessage ? { lastUserMessage: trimmed } : {}),
      lastActivityAt: userTimestamp,
    },
  });
  if (echoUserMessage) {
    params.dispatch({
      type: "appendOutput",
      agentId,
      line: `> ${trimmed}`,
      transcript: {
        source: "local-send",
        runId,
        sessionKey: params.sessionKey,
        timestampMs: optimisticUserOrderTimestamp,
        role: "user",
        kind: "user",
      },
    });
  }

  try {
    if (!params.sessionKey) {
      throw new Error("Missing session key for agent.");
    }

    let createdSession = agent.sessionCreated;
    if (!agent.sessionSettingsSynced) {
      try {
        await syncGatewaySessionSettings({
          client: params.client as unknown as GatewayClient,
          sessionKey: params.sessionKey,
          model: agent.model ?? null,
          thinkingLevel: agent.thinkingLevel ?? null,
          execHost: agent.sessionExecHost,
          execSecurity: agent.sessionExecSecurity,
          execAsk: agent.sessionExecAsk,
        });
        createdSession = true;
        params.dispatch({
          type: "updateAgent",
          agentId,
          patch: { sessionSettingsSynced: true, sessionCreated: true },
        });
      } catch (syncError) {
        if (!isWebchatSessionMutationBlockedError(syncError)) {
          throw syncError;
        }
        createdSession = true;
        params.dispatch({
          type: "updateAgent",
          agentId,
          patch: { sessionSettingsSynced: true, sessionCreated: true },
        });
      }
    }

    const sendResult = await params.client.call("chat.send", {
      sessionKey: params.sessionKey,
      message: buildAgentInstruction({ message: trimmed }),
      deliver: false,
      idempotencyKey: runId,
    });

    if (!createdSession) {
      params.dispatch({
        type: "updateAgent",
        agentId,
        patch: { sessionCreated: true },
      });
    }

    if (resolveChatSendCompletionMode(sendResult, runId) === "terminal-immediate") {
      params.dispatch({
        type: "updateAgent",
        agentId,
        patch: {
          status: "idle",
          runId: null,
          runStartedAt: null,
          streamText: null,
          thinkingTrace: null,
        },
      });
    }
  } catch (err) {
    const msg = err instanceof Error ? err.message : "Gateway error";
    params.dispatch({
      type: "updateAgent",
      agentId,
      patch: { status: "error", runId: null, runStartedAt: null, streamText: null, thinkingTrace: null },
    });
    params.dispatch({
      type: "appendOutput",
      agentId,
      line: `Error: ${msg}`,
    });
  }
}
