import { type PermissionReply } from "../runtime/opencode.js";
import {
  type PermissionRequestPayload,
  type QuestionRequestPayload,
  type TodoUpdatePayload,
} from "./chatStream.js";

export type RunStatus = "running" | "completed" | "error" | "aborted";

export type StreamSubscriber = {
  write: (event: string, data: Record<string, unknown>) => void;
  close: () => void;
};

export type ActiveRun = {
  clientSessionId: string;
  controller: AbortController;
  messages: unknown[];
  pendingPermissions: Map<string, PermissionRequestPayload>;
  pendingQuestions: Map<string, QuestionRequestPayload>;
  status: RunStatus;
  subscribers: Set<StreamSubscriber>;
};

type ToolArtifactKind = "chart" | "map" | "panel" | "tool";

type ToolCallPayload = {
  session_id?: string;
  tool?: string;
  params?: unknown;
  reason?: string;
};

export const isObjectRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null && !Array.isArray(value);

const createFrontendMessageId = () =>
  `msg-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;

export const createInitialStreamingMessages = (
  existingMessages: unknown[],
  userContent: string,
) => {
  const userMessage = {
    id: createFrontendMessageId(),
    role: "user",
    content: userContent,
  };
  return [
    ...existingMessages,
    {
      ...userMessage,
      branchRootId: userMessage.id,
    },
    {
      id: createFrontendMessageId(),
      role: "assistant",
      content: "",
      progress: [
        {
          id: "request-received",
          phase: "start",
          status: "running",
          title: "已收到请求，正在启动 Agent 分析",
          detail: "已接收用户消息，正在建立会话并准备进入分析、规划和工具调用阶段。",
          startedAt: Date.now(),
          elapsedMs: 0,
          elapsedSnapshotAt: Date.now(),
        },
      ],
    },
  ];
};

export const upsertBackendProgress = (
  progress: unknown,
  payload: Record<string, unknown>,
) => {
  const next = Array.isArray(progress) ? [...progress] : [];
  const id = typeof payload.id === "string" ? payload.id : `progress-${Date.now()}`;
  const index = next.findIndex((item) => isObjectRecord(item) && item.id === id);
  const nextItem = {
    id,
    phase: typeof payload.phase === "string" ? payload.phase : "progress",
    status:
      payload.status === "completed" || payload.status === "error"
        ? payload.status
        : "running",
    title: typeof payload.title === "string" ? payload.title : "正在处理",
    detail: typeof payload.detail === "string" ? payload.detail : undefined,
    startedAt: typeof payload.started_at === "number" ? payload.started_at : undefined,
    endedAt: typeof payload.ended_at === "number" ? payload.ended_at : undefined,
    elapsedMs: typeof payload.elapsed_ms === "number" ? payload.elapsed_ms : undefined,
    elapsedSnapshotAt:
      typeof payload.elapsed_ms === "number" ? Date.now() : undefined,
    durationMs: typeof payload.duration_ms === "number" ? payload.duration_ms : undefined,
  };
  if (index >= 0) {
    next[index] = nextItem;
  } else {
    next.push(nextItem);
  }
  return next;
};

export const completeBackendProgress = (progress: unknown) =>
  Array.isArray(progress)
    ? progress.map((item) => {
        if (!isObjectRecord(item) || item.status !== "running") {
          return item;
        }
        const endedAt = Date.now();
        const startedAt = typeof item.startedAt === "number" ? item.startedAt : undefined;
        return {
          ...item,
          status: "completed",
          endedAt,
          elapsedMs: undefined,
          elapsedSnapshotAt: undefined,
          durationMs:
            typeof item.durationMs === "number"
              ? item.durationMs
              : startedAt !== undefined
                ? Math.max(0, endedAt - startedAt)
                : item.elapsedMs,
        };
      })
    : progress;

export const cancelBackendTodos = (todos: unknown) =>
  Array.isArray(todos)
    ? todos.map((todoUpdate) => {
        if (!isObjectRecord(todoUpdate) || !Array.isArray(todoUpdate.todos)) {
          return todoUpdate;
        }
        return {
          ...todoUpdate,
          todos: todoUpdate.todos.map((todo) => {
            if (!isObjectRecord(todo)) {
              return todo;
            }
            if (todo.status !== "pending" && todo.status !== "in_progress") {
              return todo;
            }
            return {
              ...todo,
              status: "cancelled",
              updatedAt: Date.now(),
            };
          }),
        };
      })
    : todos;

export const updateLastAssistantMessage = (
  messages: unknown[],
  updater: (message: Record<string, unknown>) => Record<string, unknown>,
) => {
  for (let index = messages.length - 1; index >= 0; index -= 1) {
    const message = messages[index];
    if (isObjectRecord(message) && message.role === "assistant") {
      const next = [...messages];
      next[index] = updater(message);
      return next;
    }
  }
  return messages;
};

export const updateLastAssistantPermission = (
  messages: unknown[],
  requestId: string,
  updater: (permission: Record<string, unknown>) => Record<string, unknown>,
) =>
  updateLastAssistantMessage(messages, (message) => {
    const permissions = Array.isArray(message.permissions)
      ? message.permissions
      : [];
    return {
      ...message,
      permissions: permissions.map((permission) =>
        isObjectRecord(permission) && permission.requestId === requestId
          ? updater(permission)
          : permission,
      ),
    };
  });

export const updateLastAssistantQuestion = (
  messages: unknown[],
  requestId: string,
  updater: (question: Record<string, unknown>) => Record<string, unknown>,
) =>
  updateLastAssistantMessage(messages, (message) => {
    const questions = Array.isArray(message.questions)
      ? message.questions
      : [];
    return {
      ...message,
      questions: questions.map((question) =>
        isObjectRecord(question) && question.requestId === requestId
          ? updater(question)
          : question,
      ),
    };
  });

const getToolArtifactKind = (tool: string): ToolArtifactKind => {
  if (tool === "show_chart" || tool === "chart") return "chart";
  if (
    tool === "locate_features" ||
    tool === "zoom_to_map" ||
    tool === "render_junctions" ||
    tool === "apply_layer_style" ||
    tool.startsWith("locate_")
  ) {
    return "map";
  }
  if (tool === "view_history" || tool === "view_scada") return "panel";
  return "tool";
};

const getToolArtifactTitle = (tool: string, params: Record<string, unknown>) => {
  if (typeof params.title === "string" && params.title.trim()) {
    return params.title.trim();
  }
  if (tool === "show_chart" || tool === "chart") return "生成图表";
  if (tool === "zoom_to_map") return "缩放到地图坐标";
  if (tool === "render_junctions") return "渲染节点分区";
  if (tool === "view_history") return "打开计算结果曲线";
  if (tool === "view_scada") return "打开 SCADA 数据面板";
  if (tool === "apply_layer_style") return "应用图层样式";
  if (tool === "locate_features" || tool.startsWith("locate_")) return "地图定位";
  return tool || "工具调用";
};

export const appendBackendToolArtifact = (
  artifacts: unknown,
  payload: ToolCallPayload,
) => {
  const tool = typeof payload.tool === "string" ? payload.tool.trim() : "";
  if (!tool) {
    return artifacts;
  }
  const params = isObjectRecord(payload.params) ? payload.params : {};
  const next = Array.isArray(artifacts) ? [...artifacts] : [];
  next.push({
    id: `${tool}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`,
    tool,
    kind: getToolArtifactKind(tool),
    title: getToolArtifactTitle(tool, params),
    description:
      typeof payload.reason === "string" && payload.reason.trim()
        ? payload.reason.trim()
        : undefined,
    params,
  });
  return next;
};

export const toFrontendPermission = (
  payload: PermissionRequestPayload,
  status: "pending" | "approved_once" | "approved_always" | "rejected" | "error" = "pending",
) => ({
  requestId: payload.request_id,
  sessionId: payload.session_id,
  permission: payload.permission,
  patterns: payload.patterns,
  target: payload.target,
  always: payload.always,
  tool: payload.tool,
  createdAt: payload.created_at,
  status,
});

const toFrontendQuestion = (
  payload: QuestionRequestPayload,
  status: "pending" | "submitting" | "answered" | "rejected" | "error" = "pending",
) => ({
  requestId: payload.request_id,
  sessionId: payload.session_id,
  questions: payload.questions,
  tool: payload.tool,
  createdAt: payload.created_at,
  status,
});

export const toPermissionStatus = (reply: PermissionReply) => {
  if (reply === "always") return "approved_always";
  if (reply === "once") return "approved_once";
  return "rejected";
};

export const upsertBackendQuestion = (
  questions: unknown,
  payload: QuestionRequestPayload,
) => {
  const next = Array.isArray(questions) ? [...questions] : [];
  const index = next.findIndex((item) => {
    if (!isObjectRecord(item)) return false;
    if (item.requestId === payload.request_id) return true;
    const tool = isObjectRecord(item.tool) ? item.tool : undefined;
    return Boolean(
      payload.tool?.callID &&
        tool?.callID === payload.tool.callID,
    );
  });
  const nextItem = toFrontendQuestion(payload);
  if (index >= 0) {
    const current = next[index];
    const currentRequestId = isObjectRecord(current) ? current.requestId : undefined;
    const currentTool = isObjectRecord(current) && isObjectRecord(current.tool)
      ? current.tool
      : undefined;
    const currentIsActionable =
      typeof currentRequestId === "string" &&
      currentRequestId !== currentTool?.callID;
    const payloadIsToolPlaceholder =
      Boolean(payload.tool?.callID) && payload.request_id === payload.tool?.callID;
    next[index] = {
      ...(isObjectRecord(current) ? current : {}),
      ...(currentIsActionable && payloadIsToolPlaceholder
        ? {
            questions: nextItem.questions,
            tool: nextItem.tool,
            createdAt: nextItem.createdAt,
          }
        : nextItem),
      status:
        isObjectRecord(current) && current.status === "submitting"
          ? "submitting"
          : nextItem.status,
    };
  } else {
    next.push(nextItem);
  }
  return next;
};

export const upsertBackendTodoUpdate = (
  _todos: unknown,
  payload: TodoUpdatePayload,
) => [
  {
    sessionId: payload.session_id,
    messageId: payload.message_id,
    todos: payload.todos,
    createdAt: payload.created_at,
  },
];
