import {
  activeThreadState,
  type ChatActiveThreadState,
  type ChatState,
  createChatState,
} from "../../../../src/features/chat/application/state/root-reducer";

interface RuntimePatch {
  active?: Partial<ChatState["runtime"]["active"]>;
  pending?: Partial<ChatState["runtime"]["pending"]>;
}

interface ChatStateFixturePatch {
  connection?: Partial<ChatState["connection"]>;
  threadList?: Partial<ChatState["threadList"]>;
  activeThread?: Partial<Omit<ChatActiveThreadState, "id">> & { id?: string | null };
  runtime?: RuntimePatch;
  turn?: Partial<ChatState["turn"]>;
  threadStream?: Partial<ChatState["threadStream"]>;
  requests?: Partial<ChatState["requests"]>;
  composer?: Partial<ChatState["composer"]>;
  ui?: Partial<Omit<ChatState["ui"], "disclosures">> & {
    disclosures?: Partial<ChatState["ui"]["disclosures"]>;
  };
}

export function chatStateFixture(patch: ChatStateFixturePatch = {}): ChatState {
  return chatStateWith(createChatState(), patch);
}

export function chatStateWith(state: ChatState, patch: ChatStateFixturePatch): ChatState {
  const uiPatch = patch.ui;
  const { disclosures: disclosurePatch, ...uiFieldsPatch } = uiPatch ?? {};
  return {
    ...state,
    ...(patch.connection ? { connection: { ...state.connection, ...patch.connection } } : {}),
    ...(patch.threadList ? { threadList: { ...state.threadList, ...patch.threadList } } : {}),
    ...(patch.activeThread ? { panelThread: panelThreadWithPatch(state, patch.activeThread) } : {}),
    ...(patch.runtime ? { runtime: runtimeWithPatch(state.runtime, patch.runtime) } : {}),
    ...(patch.turn ? { turn: { ...state.turn, ...patch.turn } } : {}),
    ...(patch.threadStream ? { threadStream: { ...state.threadStream, ...patch.threadStream } } : {}),
    ...(patch.requests ? { requests: { ...state.requests, ...patch.requests } } : {}),
    ...(patch.composer ? { composer: { ...state.composer, ...patch.composer } } : {}),
    ...(uiPatch
      ? {
          ui: {
            ...state.ui,
            ...uiFieldsPatch,
            ...(disclosurePatch ? { disclosures: { ...state.ui.disclosures, ...disclosurePatch } } : {}),
          },
        }
      : {}),
  };
}

function panelThreadWithPatch(state: ChatState, patch: NonNullable<ChatStateFixturePatch["activeThread"]>): ChatState["panelThread"] {
  if (patch.id === null) {
    return { kind: "empty" };
  }
  const current = activeThreadState(state);
  const id = patch.id ?? current?.id;
  if (!id) throw new Error("An active thread fixture patch requires a thread id.");
  return {
    kind: "active",
    thread: {
      id,
      title: patch.title === undefined ? (current?.title ?? null) : patch.title,
      cwd: patch.cwd === undefined ? (current?.cwd ?? null) : patch.cwd,
      goal: patch.goal === undefined ? (current?.goal ?? null) : patch.goal,
      tokenUsage: patch.tokenUsage === undefined ? (current?.tokenUsage ?? null) : patch.tokenUsage,
      lifetime: patch.lifetime === undefined ? (current?.lifetime ?? null) : patch.lifetime,
      provenance: patch.provenance === undefined ? (current?.provenance ?? null) : patch.provenance,
    },
  };
}

function runtimeWithPatch(runtime: ChatState["runtime"], patch: RuntimePatch): ChatState["runtime"] {
  return {
    active: {
      ...runtime.active,
      ...(patch.active && "serviceTier" in patch.active ? { serviceTierKnown: true } : {}),
      ...(patch.active && "approvalPolicy" in patch.active ? { approvalPolicyKnown: true } : {}),
      ...(patch.active && "sandboxPolicy" in patch.active ? { sandboxPolicyKnown: true } : {}),
      ...(patch.active && "activePermissionProfile" in patch.active ? { permissionProfileKnown: true } : {}),
      ...patch.active,
    },
    pending: {
      ...runtime.pending,
      ...patch.pending,
    },
  };
}
