import test from "node:test";
import assert from "node:assert/strict";
import { initialAppState, reduceAppState, resolveInitialContext } from "./app-state.js";

test("app starts on chats", () => {
  assert.equal(initialAppState.tab, "threads");
});

test("initial context selects first options", () => {
  const state = reduceAppState(initialAppState, {
    type: "context.loaded",
    accounts: [{ id: "a" }],
    workspaces: [{ id: "w" }]
  });

  assert.equal(state.accountId, "a");
  assert.equal(state.workspaceId, "w");
});

test("loaded context preserves saved account and workspace when available", () => {
  const state = reduceAppState(initialAppState, {
    type: "context.loaded",
    accounts: [{ id: "a" }, { id: "b" }],
    workspaces: [{ id: "old" }, { id: "w" }],
    savedContext: { accountId: "b", workspaceId: "w" }
  });

  assert.equal(state.accountId, "b");
  assert.equal(state.workspaceId, "w");
});

test("partial context load keeps successful accounts and reports the failed side", () => {
  const state = reduceAppState(initialAppState, {
    type: "context.loaded",
    accounts: [{ id: "a" }],
    workspaces: [],
    savedContext: { accountId: "a", workspaceId: "missing" },
    error: "Workspaces unavailable"
  });

  assert.deepEqual(state.accounts, [{ id: "a" }]);
  assert.deepEqual(state.workspaces, []);
  assert.equal(state.accountId, "a");
  assert.equal(state.workspaceId, "");
  assert.equal(state.error, "Workspaces unavailable");
});

test("partial context load preserves the saved account when accounts fail", () => {
  const state = reduceAppState(initialAppState, {
    type: "context.loaded",
    accounts: [],
    workspaces: [{ id: "w" }],
    accountsLoaded: false,
    workspacesLoaded: true,
    savedContext: { accountId: "saved-account", workspaceId: "w" },
    error: "Accounts unavailable"
  });

  assert.deepEqual(state.accounts, []);
  assert.equal(state.accountId, "saved-account");
  assert.equal(state.workspaceId, "w");
  assert.equal(state.error, "Accounts unavailable");
});

test("partial context load preserves the saved workspace when workspaces fail", () => {
  const state = reduceAppState(initialAppState, {
    type: "context.loaded",
    accounts: [{ id: "a" }],
    workspaces: [],
    accountsLoaded: true,
    workspacesLoaded: false,
    savedContext: { accountId: "a", workspaceId: "saved-workspace" },
    error: "Projects unavailable"
  });

  assert.equal(state.accountId, "a");
  assert.equal(state.workspaceId, "saved-workspace");
  assert.equal(state.error, "Projects unavailable");
});

test("initial context ignores stale saved ids and chooses valid fallbacks", () => {
  assert.deepEqual(resolveInitialContext({
    accounts: [{ id: "a" }, { id: "b" }],
    workspaces: [{ id: "w" }],
    savedContext: { accountId: "removed", workspaceId: "missing" }
  }), { accountId: "a", workspaceId: "w" });
});

test("opened session preserves the selected execution account and selects its workspace and chat", () => {
  const state = reduceAppState(
    { ...initialAppState, accountId: "account-a", tab: "threads" },
    { type: "session.opened", session: { id: "s", accountId: "account-b", workspaceId: "w" } }
  );

  assert.equal(state.session.id, "s");
  assert.equal(state.accountId, "account-a");
  assert.equal(state.workspaceId, "w");
  assert.equal(state.tab, "chat");
});

test("opened unknown-workspace session requires a fresh workspace choice", () => {
  const state = reduceAppState(
    { ...initialAppState, accountId: "account-a", workspaceId: "previous", tab: "threads" },
    { type: "session.opened", session: { id: "s", accountId: "account-b", workspaceId: "" } }
  );

  assert.equal(state.session.id, "s");
  assert.equal(state.accountId, "account-a");
  assert.equal(state.workspaceId, "");
  assert.equal(state.tab, "chat");
});

test("chat transition opens the destination screen before async work finishes", () => {
  const target = { accountId: "account-b", workspaceId: "workspace-b", projectless: false };
  const state = reduceAppState(
    { ...initialAppState, tab: "threads", session: { id: "old" } },
    { type: "chat.transition.started", requestId: 7, label: "Project Alpha", target }
  );

  assert.equal(state.tab, "chat");
  assert.deepEqual(state.chatTransition, {
    requestId: 7,
    status: "loading",
    label: "Project Alpha",
    target,
    error: ""
  });
  assert.equal(state.session.id, "old");
});

test("only the latest chat transition may fail or open a session", () => {
  const loading = {
    ...initialAppState,
    tab: "chat",
    chatTransition: { requestId: 8, status: "loading", label: "Newest", error: "" }
  };
  const staleFailure = reduceAppState(loading, {
    type: "chat.transition.failed",
    requestId: 7,
    error: "stale"
  });
  const staleSuccess = reduceAppState(loading, {
    type: "session.opened",
    requestId: 7,
    session: { id: "stale" }
  });
  const currentFailure = reduceAppState(loading, {
    type: "chat.transition.failed",
    requestId: 8,
    error: "offline"
  });

  assert.equal(staleFailure, loading);
  assert.equal(staleSuccess, loading);
  assert.equal(currentFailure.chatTransition.status, "failed");
  assert.equal(currentFailure.chatTransition.error, "offline");
});

test("current session open clears chat transition", () => {
  const state = reduceAppState(
    {
      ...initialAppState,
      chatTransition: { requestId: 3, status: "loading", label: "Chat", error: "" }
    },
    { type: "session.opened", requestId: 3, session: { id: "ready", workspaceId: "w" } }
  );

  assert.equal(state.session.id, "ready");
  assert.equal(state.chatTransition, null);
});

test("settings stays open when a pending chat finishes loading", () => {
  const state = reduceAppState(
    {
      ...initialAppState,
      tab: "settings",
      settingsReturnTab: "chat",
      chatTransition: { requestId: 3, status: "loading", label: "Chat", error: "" }
    },
    { type: "session.opened", requestId: 3, session: { id: "ready", workspaceId: "w" } }
  );

  assert.equal(state.tab, "settings");
  assert.equal(state.settingsReturnTab, "chat");
  assert.equal(state.session.id, "ready");
  assert.equal(state.chatTransition, null);
});

test("explicit session reveal leaves settings and opens the chat", () => {
  const state = reduceAppState(
    {
      ...initialAppState,
      tab: "settings",
      settingsReturnTab: "threads",
      session: { id: "old" }
    },
    {
      type: "session.opened",
      session: { id: "notified", workspaceId: "w" },
      reveal: true
    }
  );

  assert.equal(state.tab, "chat");
  assert.equal(state.settingsReturnTab, null);
  assert.equal(state.session.id, "notified");
});

test("settings restores its exact source screen", () => {
  const chat = reduceAppState(
    { ...initialAppState, tab: "chat", session: { id: "ready" } },
    { type: "settings.opened" }
  );
  const restoredChat = reduceAppState(chat, { type: "settings.dismissed" });
  const newChat = reduceAppState(
    { ...initialAppState, tab: "threads", newChatOpen: true },
    { type: "settings.opened" }
  );
  const restoredNewChat = reduceAppState(newChat, { type: "settings.dismissed" });

  assert.equal(chat.tab, "settings");
  assert.equal(chat.settingsReturnTab, "chat");
  assert.equal(restoredChat.tab, "chat");
  assert.equal(newChat.tab, "settings");
  assert.equal(newChat.newChatOpen, true);
  assert.equal(restoredNewChat.tab, "threads");
  assert.equal(restoredNewChat.newChatOpen, true);
});

test("late startup restoration cannot close settings", () => {
  const settings = {
    ...initialAppState,
    tab: "settings",
    settingsReturnTab: "chat",
    session: { id: "old" }
  };
  const restored = reduceAppState(settings, {
    type: "session.restored",
    session: { id: "ready", workspaceId: "w" },
    tab: "chat"
  });
  const failed = reduceAppState(settings, { type: "session.restore.failed" });
  const restoredView = reduceAppState(settings, { type: "view.restored", tab: "threads" });

  assert.equal(restored.tab, "settings");
  assert.equal(restored.settingsReturnTab, "chat");
  assert.equal(failed.tab, "settings");
  assert.equal(failed.settingsReturnTab, "threads");
  assert.equal(restoredView, settings);
});

test("stale startup restore cannot replace or clear a newer chat behind settings", () => {
  const settings = {
    ...initialAppState,
    tab: "settings",
    settingsReturnTab: "chat",
    session: { id: "new-chat", workspaceId: "new-workspace" }
  };
  const staleSuccess = reduceAppState(settings, {
    type: "session.restored",
    restoreSessionId: "old-chat",
    session: { id: "old-chat", workspaceId: "old-workspace" },
    tab: "chat"
  });
  const staleFailure = reduceAppState(settings, {
    type: "session.restore.failed",
    restoreSessionId: "old-chat"
  });

  assert.equal(staleSuccess, settings);
  assert.equal(staleFailure, settings);
});

test("canceling a chat transition returns to chats and clears pending UI", () => {
  const state = reduceAppState(
    {
      ...initialAppState,
      tab: "chat",
      chatTransition: { requestId: 4, status: "loading", label: "Chat", error: "" }
    },
    { type: "chat.transition.canceled" }
  );

  assert.equal(state.tab, "threads");
  assert.equal(state.chatTransition, null);
});

test("restored session returns to the saved chat screen", () => {
  const state = reduceAppState(
    { ...initialAppState, accountId: "a", workspaceId: "old", tab: "threads" },
    { type: "session.restored", session: { id: "s", accountId: "b", workspaceId: "w" }, tab: "chat" }
  );

  assert.equal(state.session.id, "s");
  assert.equal(state.accountId, "a");
  assert.equal(state.workspaceId, "w");
  assert.equal(state.tab, "chat");
});

test("restored unknown-workspace session requires a fresh workspace choice", () => {
  const state = reduceAppState(
    { ...initialAppState, accountId: "a", workspaceId: "previous", tab: "threads" },
    { type: "session.restored", session: { id: "s", accountId: "b", workspaceId: "" }, tab: "chat" }
  );

  assert.equal(state.session.id, "s");
  assert.equal(state.accountId, "a");
  assert.equal(state.workspaceId, "");
  assert.equal(state.tab, "chat");
});

test("failed session restore returns to chats and clears stale session UI", () => {
  const state = reduceAppState(
    {
      ...initialAppState,
      tab: "chat",
      session: { id: "stale" },
      chatTransition: { requestId: 1, status: "loading", label: "Old chat", error: "" }
    },
    { type: "session.restore.failed" }
  );

  assert.equal(state.tab, "threads");
  assert.equal(state.session, null);
  assert.equal(state.chatTransition, null);
});

test("new chat action opens project selection without creating a session", () => {
  const state = reduceAppState(
    { ...initialAppState, session: { id: "old" } },
    { type: "new-chat.selected" }
  );

  assert.equal(state.tab, "threads");
  assert.equal(state.newChatOpen, true);
  assert.equal(state.session.id, "old");
});

test("opening another screen dismisses the new chat popup", () => {
  const state = reduceAppState(
    { ...initialAppState, tab: "threads", newChatOpen: true },
    { type: "tab.selected", tab: "settings" }
  );

  assert.equal(state.tab, "settings");
  assert.equal(state.newChatOpen, false);
});

test("restoring a new chat view reopens it as a popup over chats", () => {
  const state = reduceAppState(initialAppState, { type: "view.restored", tab: "new" });

  assert.equal(state.tab, "threads");
  assert.equal(state.newChatOpen, true);
});

test("restored settings view opens the settings screen", () => {
  const state = reduceAppState(initialAppState, { type: "view.restored", tab: "settings" });

  assert.equal(state.tab, "settings");
});

test("replaced accounts select preferred new account", () => {
  const state = reduceAppState(
    { ...initialAppState, accounts: [{ id: "a" }], accountId: "a" },
    { type: "accounts.replaced", accounts: [{ id: "a" }, { id: "b" }], preferredId: "b" }
  );
  assert.equal(state.accountId, "b");
});

test("replaced accounts fall back when selected account was deleted", () => {
  const state = reduceAppState(
    { ...initialAppState, accounts: [{ id: "a" }, { id: "b" }], accountId: "b" },
    { type: "accounts.replaced", accounts: [{ id: "a" }] }
  );
  assert.equal(state.accountId, "a");
});

test("replaced accounts preserve current selection when it remains", () => {
  const state = reduceAppState(
    { ...initialAppState, accounts: [{ id: "a" }, { id: "b" }], accountId: "b" },
    { type: "accounts.replaced", accounts: [{ id: "a" }, { id: "b" }] }
  );
  assert.equal(state.accountId, "b");
});

test("replaced workspaces selects the newly registered project", () => {
  const state = reduceAppState(
    { ...initialAppState, workspaces: [{ id: "old", label: "Old" }], workspaceId: "old" },
    { type: "workspaces.replaced", workspaces: [{ id: "old", label: "Old" }, { id: "new", label: "New" }], preferredId: "new" }
  );
  assert.equal(state.workspaceId, "new");
  assert.equal(state.workspaces[1].label, "New");
});

test("session update selects the attached workspace", () => {
  const state = reduceAppState(
    { ...initialAppState, workspaceId: "", session: { id: "s", workspaceId: "" } },
    { type: "session.updated", session: { id: "s", workspaceId: "attached" } }
  );

  assert.equal(state.session.workspaceId, "attached");
  assert.equal(state.workspaceId, "attached");
});
