import { mergeThreadPage } from "../../thread-library.js";

export const initialChatsState = {
  threads: [],
  recentProjectThreads: [],
  projectThreads: [],
  hiddenProjects: [],
  projectsLoaded: false,
  projectError: "",
  nextCursor: null,
  loading: false,
  error: "",
  queryScope: null
};

const safeChatErrors = new Set([
  "chat_archive_failed",
  "chat_create_failed",
  "chat_delete_failed",
  "chat_message_failed",
  "chat_open_failed",
  "chat_pin_failed",
  "chat_rename_failed",
  "chats_partial_unavailable",
  "chats_unavailable"
]);

export function chatOperationError(error, fallback) {
  if (safeChatErrors.has(error?.message)) return error.message;
  return fallback;
}

export function assertSessionWorkspace(result, expectedWorkspaceId) {
  const actual = result?.session?.workspaceId ?? "";
  const expected = expectedWorkspaceId ?? "";
  if (actual !== expected) throw new Error("workspace_mismatch");
}

export function reduceChatsState(state, action) {
  if (action.type === "account.changed") return state;
  if (action.type === "page.loading" && action.silent) return state;
  if (action.type === "page.loading") return {
    ...state,
    ...(state.queryScope !== action.queryScope ? {
      threads: [],
      nextCursor: null
    } : {}),
    queryScope: action.queryScope,
    loading: true,
    error: ""
  };
  if (action.type === "page.loaded") return {
    ...state,
    threads: mergeThreadPage(action.append ? state.threads : [], action.threads),
    recentProjectThreads: action.queryScope === "recent" && !action.searching && !action.append
      ? mergeThreadPage([], action.threads ?? [])
      : state.recentProjectThreads,
    nextCursor: action.nextCursor ?? null,
    queryScope: action.queryScope ?? state.queryScope,
    loading: false,
    error: (action.errors ?? []).length > 0 ? "chats_partial_unavailable" : ""
  };
  if (action.type === "projects.loaded") {
    const partial = (action.errors ?? []).length > 0;
    if (partial && state.projectsLoaded) return { ...state, projectError: "chats_partial_unavailable" };
    return {
      ...state,
      projectThreads: mergeThreadPage(action.append ? state.projectThreads : [], action.threads ?? []),
      hiddenProjects: Array.isArray(action.hiddenProjects) ? action.hiddenProjects : [],
      projectsLoaded: true,
      projectError: partial ? "chats_partial_unavailable" : ""
    };
  }
  if (action.type === "projects.hidden") return {
    ...state,
    hiddenProjects: [
      ...state.hiddenProjects.filter((item) => item.projectKey !== action.hiddenProject.projectKey),
      action.hiddenProject
    ]
  };
  if (action.type === "projects.failed") return { ...state, projectError: action.error };
  if (action.type === "page.failed") return { ...state, loading: false, error: action.error };
  if (action.type === "thread.renamed") return {
    ...state,
    threads: updateOwnedThread(state.threads, action, (thread) => ({ ...thread, name: action.name })),
    projectThreads: updateOwnedThread(state.projectThreads, action, (thread) => ({ ...thread, name: action.name }))
  };
  if (action.type === "thread.pinned") return {
    ...state,
    threads: updateOwnedThread(state.threads, action, (thread) => ({ ...thread, pinned: action.pinned })),
    projectThreads: updateOwnedThread(state.projectThreads, action, (thread) => ({ ...thread, pinned: action.pinned }))
  };
  if (action.type === "thread.archived") return {
    ...state,
    threads: state.threads.filter((thread) => !matchesOwnedThread(thread, action)),
    projectThreads: state.projectThreads.filter((thread) => !matchesOwnedThread(thread, action))
  };
  if (action.type === "archive.started") return {
    ...state,
    error: "",
    threads: state.threads.filter((thread) => !(
      thread.ownerId === action.thread.ownerId && thread.id === action.thread.id
    )),
    projectThreads: state.projectThreads.filter((thread) => !(
      thread.ownerId === action.thread.ownerId && thread.id === action.thread.id
    ))
  };
  if (action.type === "archive.failed") return {
    ...state,
    threads: mergeThreadPage(state.threads, [action.thread]),
    projectThreads: mergeThreadPage(state.projectThreads, [action.thread]),
    error: action.error
  };
  if (action.type === "delete.started") return {
    ...state,
    error: "",
    threads: state.threads.filter((thread) => !(
      thread.ownerId === action.thread.ownerId && thread.id === action.thread.id
    )),
    projectThreads: state.projectThreads.filter((thread) => !(
      thread.ownerId === action.thread.ownerId && thread.id === action.thread.id
    ))
  };
  if (action.type === "delete.failed") return {
    ...state,
    threads: mergeThreadPage(state.threads, [action.thread]),
    projectThreads: mergeThreadPage(state.projectThreads, [action.thread]),
    error: action.error
  };
  return state;
}

function updateOwnedThread(threads, action, update) {
  return threads.map((thread) => matchesOwnedThread(thread, action) ? update(thread) : thread);
}

function matchesOwnedThread(thread, action) {
  return thread.ownerId === action.ownerId && thread.id === action.threadId;
}
