import type {
  AgentChatMessage,
  AgentChatProgress,
  AgentInteractionToolRef,
  AgentPermissionRequest,
  AgentPermissionStatus,
  AgentQuestionInfo,
  AgentQuestionRequest,
  AgentTodoItem,
  AgentTodoUpdate
} from "./types";

type RecordValue = Record<string, unknown>;

export function upsertProgress(progress: AgentChatProgress[] | undefined, data: unknown) {
  const payload = asRecord(data);
  const id = readString(payload.id) ?? `progress-${Date.now().toString(36)}`;
  const now = Date.now();
  const index = progress?.findIndex((item) => item.id === id) ?? -1;
  const existing = index >= 0 ? progress?.[index] : undefined;
  const status = readProgressStatus(payload.status);
  const startedAt = readNumber(payload.started_at) ?? readNumber(payload.startedAt) ?? existing?.startedAt;
  const endedAt = readNumber(payload.ended_at) ?? readNumber(payload.endedAt);
  const elapsedMs = readNumber(payload.elapsed_ms) ?? readNumber(payload.elapsedMs);
  const durationMs = readNumber(payload.duration_ms) ?? readNumber(payload.durationMs);
  const nextItem: AgentChatProgress = {
    id,
    phase: readString(payload.phase) ?? "progress",
    status,
    title: readString(payload.title) ?? "正在处理",
    detail: readString(payload.detail),
    startedAt,
    endedAt,
    elapsedMs: status === "running" ? elapsedMs : undefined,
    elapsedSnapshotAt: status === "running" && elapsedMs !== undefined ? now : undefined,
    durationMs: status === "running" ? undefined : durationMs
  };
  const next = [...(progress ?? [])];

  if (index >= 0) {
    next[index] = nextItem;
  } else {
    next.push(nextItem);
  }

  return next;
}

export function completeRunningProgress(progress: AgentChatProgress[] | undefined) {
  return progress?.map((item) => {
    if (item.status !== "running") {
      return item;
    }

    const endedAt = Date.now();
    return {
      ...item,
      status: "completed" as const,
      endedAt,
      elapsedMs: undefined,
      elapsedSnapshotAt: undefined,
      durationMs:
        item.durationMs ??
        (item.startedAt !== undefined ? Math.max(0, endedAt - item.startedAt) : item.elapsedMs)
    };
  });
}

export function toTodoUpdate(data: unknown): AgentTodoUpdate | null {
  const payload = asRecord(data);
  const sessionId = readString(payload.session_id) ?? readString(payload.sessionId);
  const rawTodos = Array.isArray(payload.todos) ? payload.todos : [];

  if (!sessionId || rawTodos.length === 0) {
    return null;
  }

  return {
    sessionId,
    messageId: readString(payload.message_id) ?? readString(payload.messageId),
    todos: rawTodos.map(toTodoItem).filter((item): item is AgentTodoItem => Boolean(item)),
    createdAt: readNumber(payload.created_at) ?? readNumber(payload.createdAt) ?? Date.now()
  };
}

export function cancelRunningTodos(todoUpdate: AgentTodoUpdate | undefined) {
  return todoUpdate
    ? {
        ...todoUpdate,
        todos: todoUpdate.todos.map((todo) =>
          todo.status === "pending" || todo.status === "in_progress"
            ? {
                ...todo,
                status: "cancelled" as const,
                updatedAt: Date.now()
              }
            : todo
        )
      }
    : undefined;
}

export function upsertPermission(
  permissions: AgentPermissionRequest[] | undefined,
  data: unknown
) {
  const payload = asRecord(data);
  const requestId = readString(payload.request_id) ?? readString(payload.requestId);
  const sessionId = readString(payload.session_id) ?? readString(payload.sessionId);

  if (!requestId || !sessionId) {
    return permissions;
  }

  const nextItem: AgentPermissionRequest = {
    requestId,
    sessionId,
    permission: readString(payload.permission) ?? "unknown",
    patterns: readStringArray(payload.patterns),
    target: readString(payload.target),
    always: readStringArray(payload.always),
    tool: toToolRef(payload.tool),
    createdAt: readNumber(payload.created_at) ?? readNumber(payload.createdAt) ?? Date.now(),
    status: "pending"
  };
  const next = [...(permissions ?? [])];
  const index = next.findIndex((permission) => permission.requestId === requestId);

  if (index >= 0) {
    next[index] = {
      ...next[index],
      ...nextItem,
      status: next[index].status === "submitting" ? "submitting" : nextItem.status
    };
  } else {
    next.push(nextItem);
  }

  return next;
}

export function applyPermissionResponse(
  permissions: AgentPermissionRequest[] | undefined,
  data: unknown
) {
  const payload = asRecord(data);
  const requestId = readString(payload.request_id) ?? readString(payload.requestId);
  if (!requestId) {
    return permissions;
  }

  return permissions?.map((permission) =>
    permission.requestId === requestId
      ? {
          ...permission,
          status: toPermissionStatus(readString(payload.reply)),
          repliedAt: Date.now(),
          error: undefined
        }
      : permission
  );
}

export function upsertQuestion(questions: AgentQuestionRequest[] | undefined, data: unknown) {
  const payload = asRecord(data);
  const requestId = readString(payload.request_id) ?? readString(payload.requestId);
  const sessionId = readString(payload.session_id) ?? readString(payload.sessionId);
  const rawQuestions = Array.isArray(payload.questions) ? payload.questions : [];

  if (!requestId || !sessionId || rawQuestions.length === 0) {
    return questions;
  }

  const nextItem: AgentQuestionRequest = {
    requestId,
    sessionId,
    questions: rawQuestions.map(toQuestionInfo).filter((item): item is AgentQuestionInfo => Boolean(item)),
    tool: toToolRef(payload.tool),
    createdAt: readNumber(payload.created_at) ?? readNumber(payload.createdAt) ?? Date.now(),
    status: "pending"
  };
  const next = [...(questions ?? [])];
  const index = next.findIndex((question) => isSameQuestion(question, nextItem));

  if (index >= 0) {
    const current = next[index];
    const currentIsActionable = Boolean(current.tool?.callID && current.requestId !== current.tool.callID);
    const nextIsToolPlaceholder = Boolean(nextItem.tool?.callID && nextItem.requestId === nextItem.tool.callID);

    next[index] =
      currentIsActionable && nextIsToolPlaceholder
        ? {
            ...current,
            sessionId: nextItem.sessionId,
            questions: nextItem.questions,
            tool: nextItem.tool ?? current.tool,
            createdAt: nextItem.createdAt
          }
        : {
            ...current,
            ...nextItem,
            status: current.status === "submitting" ? "submitting" : nextItem.status
          };
  } else {
    next.push(nextItem);
  }

  return next;
}

export function applyQuestionResponse(questions: AgentQuestionRequest[] | undefined, data: unknown) {
  const payload = asRecord(data);
  const requestId = readString(payload.request_id) ?? readString(payload.requestId);
  if (!requestId) {
    return questions;
  }

  const answers = Array.isArray(payload.answers)
    ? payload.answers.map((answer) => (Array.isArray(answer) ? answer.map(String) : []))
    : undefined;
  const rejected = payload.rejected === true;

  return questions?.map((question) =>
    question.requestId === requestId
      ? {
          ...question,
          status: rejected ? ("rejected" as const) : ("answered" as const),
          answers: answers ?? question.answers,
          repliedAt: Date.now(),
          error: undefined
        }
      : question
  );
}

export function finalizeAssistantMessageAfterAbort(message: AgentChatMessage): AgentChatMessage {
  const progress = completeRunningProgress(message.progress);
  const todos = cancelRunningTodos(message.todos);

  return {
    ...message,
    content: message.content || "请求已中止。",
    progress,
    todos,
    permissions: message.permissions?.map((permission) =>
      permission.status === "pending" || permission.status === "submitting" || permission.status === "error"
        ? {
            ...permission,
            status: "aborted" as const,
            repliedAt: Date.now(),
            error: undefined
          }
        : permission
    ),
    questions: message.questions?.map((question) =>
      question.status === "pending" || question.status === "submitting" || question.status === "error"
        ? {
            ...question,
            status: "rejected" as const,
            repliedAt: Date.now(),
            error: undefined
          }
        : question
    )
  };
}

function toPermissionStatus(reply: string | undefined): AgentPermissionStatus {
  if (reply === "always") {
    return "approved_always";
  }
  if (reply === "once") {
    return "approved_once";
  }
  return "rejected";
}

function isSameQuestion(left: AgentQuestionRequest, right: AgentQuestionRequest) {
  if (left.requestId === right.requestId) {
    return true;
  }
  return Boolean(left.tool?.callID && right.tool?.callID && left.tool.callID === right.tool.callID);
}

function toQuestionInfo(value: unknown): AgentQuestionInfo | null {
  const payload = asRecord(value);
  const question = readString(payload.question);
  if (!question) {
    return null;
  }

  return {
    header: readString(payload.header) ?? "",
    question,
    options: Array.isArray(payload.options)
      ? payload.options
          .map((option) => {
            const item = asRecord(option);
            const label = readString(item.label);
            return label
              ? {
                  label,
                  description: readString(item.description) ?? ""
                }
              : null;
          })
          .filter((item): item is { label: string; description: string } => Boolean(item))
      : [],
    multiple: typeof payload.multiple === "boolean" ? payload.multiple : undefined,
    custom: typeof payload.custom === "boolean" ? payload.custom : undefined
  };
}

function toTodoItem(value: unknown): AgentTodoItem | null {
  const payload = asRecord(value);
  const id = readString(payload.id);
  const content = readString(payload.content);
  const status = readTodoStatus(payload.status);

  if (!id || !content || !status) {
    return null;
  }

  return {
    id,
    content,
    status,
    priority: readTodoPriority(payload.priority),
    createdAt: readNumber(payload.created_at) ?? readNumber(payload.createdAt),
    updatedAt: readNumber(payload.updated_at) ?? readNumber(payload.updatedAt)
  };
}

function toToolRef(value: unknown): AgentInteractionToolRef | undefined {
  const payload = asRecord(value);
  const messageID = readString(payload.messageID);
  const callID = readString(payload.callID);
  return messageID && callID ? { messageID, callID } : undefined;
}

function readProgressStatus(value: unknown): AgentChatProgress["status"] {
  return value === "completed" || value === "error" ? value : "running";
}

function readTodoStatus(value: unknown): AgentTodoItem["status"] | undefined {
  if (value === "pending" || value === "in_progress" || value === "completed" || value === "cancelled") {
    return value;
  }
  return undefined;
}

function readTodoPriority(value: unknown): AgentTodoItem["priority"] | undefined {
  if (value === "low" || value === "medium" || value === "high") {
    return value;
  }
  return undefined;
}

function asRecord(value: unknown): RecordValue {
  return typeof value === "object" && value !== null && !Array.isArray(value)
    ? (value as RecordValue)
    : {};
}

function readString(value: unknown) {
  return typeof value === "string" && value.trim() ? value.trim() : undefined;
}

function readNumber(value: unknown) {
  return typeof value === "number" && Number.isFinite(value) ? value : undefined;
}

function readStringArray(value: unknown) {
  return Array.isArray(value) ? value.map((item) => String(item).trim()).filter(Boolean) : [];
}
