import { describe, expect, test, vi, beforeEach } from "vitest";
import { setActivePinia, createPinia } from "pinia";
import { nextTick } from "vue";

// vi.hoisted ensures the spy is created before the vi.mock factory runs.
const syncFontSizeMock = vi.hoisted(() => vi.fn());
const scheduleAllVisibleResizeMock = vi.hoisted(() => vi.fn());

vi.mock("../app/terminal-controller.js", () => ({
  createTerminalController: () => ({
    syncFontSize: syncFontSizeMock,
    syncTheme: vi.fn(),
    handleTerminalData: vi.fn(),
    handleTerminalExit: vi.fn(),
    attachTerminalPane: vi.fn(),
    focusActiveTerminal: vi.fn(),
    scheduleActiveResize: vi.fn(),
    scheduleAllVisibleResize: scheduleAllVisibleResizeMock,
    pruneTerminalViews: vi.fn(),
    exportTerminalTranscript: vi.fn(),
    clearTerminalViewport: vi.fn(),
    disconnectHiddenPaneObservers: vi.fn(),
  }),
}));

import { useTerminalStore } from "./terminal.js";
import { useAppStore } from "./app.js";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AnyRecord = Record<string, any>;

function makeApi(isRemote: boolean) {
  const connectionHandlers: Array<(payload: AnyRecord) => void> = [];
  return {
    isRemote,
    onTerminalData: vi.fn(),
    onTerminalExit: vi.fn(),
    onConnectionState: vi.fn((handler: (payload: AnyRecord) => void) => {
      connectionHandlers.push(handler);
    }),
    emitConnectionState: (payload: AnyRecord) => {
      for (const handler of connectionHandlers) handler(payload);
    },
  };
}

function initStore(isRemote: boolean) {
  const termStore = useTerminalStore();
  termStore.init(
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    makeApi(isRemote) as any,
    {},
    {
      getActiveSessionId: () => null,
      getOverlay: () => null,
      getPayload: () => null,
    },
  );
  return termStore;
}

describe("useTerminalStore — font size watch", () => {
  beforeEach(() => {
    setActivePinia(createPinia());
    (window as AnyRecord).strideterm = { startupFlags: { windowId: "" } };
    syncFontSizeMock.mockClear();
    scheduleAllVisibleResizeMock.mockClear();
    vi.useRealTimers();
  });

  test("desktop: calls syncFontSize when terminalFontSizeLocal changes", async () => {
    initStore(false);
    const appStore = useAppStore();
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    appStore.payload = { appState: { settings: { terminalFontSizeLocal: 18 } } } as any;
    await nextTick();
    expect(syncFontSizeMock).toHaveBeenCalledWith(18);
  });

  test("desktop: does not call syncFontSize when only terminalFontSizeRemote changes", async () => {
    initStore(false);
    const appStore = useAppStore();
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    appStore.payload = { appState: { settings: { terminalFontSizeRemote: 22 } } } as any;
    await nextTick();
    expect(syncFontSizeMock).not.toHaveBeenCalled();
  });

  test("remote: calls syncFontSize when terminalFontSizeRemote changes", async () => {
    initStore(true);
    const appStore = useAppStore();
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    appStore.payload = { appState: { settings: { terminalFontSizeRemote: 20 } } } as any;
    await nextTick();
    expect(syncFontSizeMock).toHaveBeenCalledWith(20);
  });

  test("remote: does not call syncFontSize when only terminalFontSizeLocal changes", async () => {
    initStore(true);
    const appStore = useAppStore();
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    appStore.payload = { appState: { settings: { terminalFontSizeLocal: 16 } } } as any;
    await nextTick();
    expect(syncFontSizeMock).not.toHaveBeenCalled();
  });

  test("remote: forces visible terminal resize after websocket reconnect", () => {
    vi.useFakeTimers();
    const termStore = useTerminalStore();
    const api = makeApi(true);
    termStore.init(
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      api as any,
      {},
      {
        getActiveSessionId: () => null,
        getOverlay: () => null,
        getPayload: () => null,
      },
    );

    api.emitConnectionState({ connected: true, reconnected: true });

    expect(scheduleAllVisibleResizeMock).toHaveBeenCalledTimes(1);
    vi.advanceTimersByTime(150);
    expect(scheduleAllVisibleResizeMock).toHaveBeenCalledTimes(2);
  });
});
