import { afterEach, describe, expect, it, vi } from "vitest"

const originalArgv = [...process.argv]

async function flushPromises() {
  await Promise.resolve()
  await Promise.resolve()
  await Promise.resolve()
}

async function loadIndexForHubInstall(
  argv: string[],
  configOverrides: Record<string, unknown> = {},
  gotSingleInstanceLock = true,
  appOptions: { isPackaged?: boolean } = {},
) {
  vi.resetModules()
  process.argv = argv

  const handlers = new Map<string, Function[]>()
  const createMainWindow = vi.fn()
  const createPanelWindow = vi.fn()
  const createSetupWindow = vi.fn()
  const showMainWindow = vi.fn()
  const getTextInputPanelHideActivationSuppressionDebugState = vi.fn(() => ({ active: false, ageMs: null, windowMs: 300 }))
  const shouldSuppressMainWindowActivationForRecentTextInputHide = vi.fn(() => false)
  const stopListeningToKeyboardEvents = vi.fn(() => Promise.resolve())
  const state = { isTextInputActive: false }
  const requestSingleInstanceLock = vi.fn(() => gotSingleInstanceLock)
  const releaseSingleInstanceLock = vi.fn()
  const quit = vi.fn()
  const findHubBundleInstallBundleUrl = vi.fn(
    (candidates: readonly string[]) => {
      const deepLink = candidates.find(
        (candidate) =>
          typeof candidate === "string" &&
          candidate.startsWith("dotagents://install?bundle="),
      )
      return deepLink
        ? "https://hub.dotagentsprotocol.com/bundles/featured-agent.dotagents"
        : null
    },
  )
  const downloadHubBundleToTempFile = vi.fn(
    async () => "/tmp/downloaded-featured-agent.dotagents",
  )
  const findHubBundleHandoffFilePath = vi.fn(
    (candidates: readonly string[]) =>
      candidates.find(
        (candidate) =>
          typeof candidate === "string" &&
          candidate.endsWith(".dotagents") &&
          !candidate.startsWith("dotagents://"),
      ) ?? null,
  )

  vi.doMock("electron", () => ({
    app: {
      commandLine: { appendSwitch: vi.fn() },
      requestSingleInstanceLock,
      releaseSingleInstanceLock,
      whenReady: vi.fn(() => Promise.resolve()),
      on: vi.fn((event: string, handler: Function) => {
        handlers.set(event, [...(handlers.get(event) ?? []), handler])
      }),
      isReady: vi.fn(() => true),
      setLoginItemSettings: vi.fn(),
      setActivationPolicy: vi.fn(),
      dock: { show: vi.fn(), hide: vi.fn(), isVisible: vi.fn(() => true) },
      isPackaged: appOptions.isPackaged ?? true,
      quit,
    },
    Menu: { setApplicationMenu: vi.fn() },
  }))
  vi.doMock("@electron-toolkit/utils", () => ({
    electronApp: { setAppUserModelId: vi.fn() },
    optimizer: { watchWindowShortcuts: vi.fn() },
  }))
  vi.doMock("@egoist/tipc/main", () => ({ registerIpcMain: vi.fn() }))
  const windows = new Map()

  vi.doMock("./window", () => ({
    createMainWindow,
    createPanelWindow,
    createSetupWindow,
    getTextInputPanelHideActivationSuppressionDebugState,
    getWindowFocusDebugSnapshot: vi.fn(() => ({ focusedWindow: "none" })),
    makePanelWindowClosable: vi.fn(),
    setAppQuitting: vi.fn(),
    shouldSuppressMainWindowActivationForRecentTextInputHide,
    showMainWindow,
    WINDOWS: windows,
  }))
  vi.doMock("./keyboard", () => ({
    listenToKeyboardEvents: vi.fn(),
    stopListeningToKeyboardEvents,
  }))
  vi.doMock("./tipc", () => ({ router: {} }))
  vi.doMock("./serve", () => ({
    registerServeProtocol: vi.fn(),
    registerServeSchema: vi.fn(),
  }))
  vi.doMock("./menu", () => ({ createAppMenu: vi.fn(() => null) }))
  vi.doMock("./tray", () => ({ initTray: vi.fn(), destroyTray: vi.fn() }))
  vi.doMock("./utils", () => ({ isAccessibilityGranted: vi.fn(() => true) }))
  vi.doMock("./mcp-service", () => ({
    mcpService: {
      initialize: vi.fn(() => Promise.resolve()),
      cleanup: vi.fn(() => Promise.resolve()),
    },
  }))
  vi.doMock("./debug", () => ({ initDebugFlags: vi.fn(), logApp: vi.fn() }))
  vi.doMock("./oauth-deeplink-handler", () => ({
    initializeDeepLinkHandling: vi.fn(),
  }))
  vi.doMock("./diagnostics", () => ({
    diagnosticsService: { logError: vi.fn() },
  }))
  vi.doMock("./config", () => ({
    configStore: {
      get: vi.fn(() => ({
        onboardingCompleted: true,
        modelPresets: [],
        currentModelPresetId: undefined,
        hideDockIcon: false,
        launchAtLogin: false,
        remoteServerEnabled: false,
        cloudflareTunnelAutoStart: false,
        ...configOverrides,
      })),
    },
    dataFolder: "/tmp/dotagents-hub-install-test",
    recordingsFolder: "/tmp/dotagents-hub-install-test/recordings",
    conversationsFolder: "/tmp/dotagents-hub-install-test/conversations",
  }))
  vi.doMock("./discord-service", () => ({
    discordService: {
      start: vi.fn(() => Promise.resolve({ success: true })),
      stop: vi.fn(() => Promise.resolve()),
      getStatus: vi.fn(() => ({ connected: false })),
    },
  }))
  vi.doMock("./remote-server", () => ({
    startRemoteServer: vi.fn(() => Promise.resolve({ running: false })),
    printQRCodeToTerminal: vi.fn(),
    startRemoteServerForced: vi.fn(() => Promise.resolve({ running: false })),
    stopRemoteServer: vi.fn(() => Promise.resolve()),
  }))
  vi.doMock("./acp-service", () => ({
    acpService: {
      initialize: vi.fn(() => Promise.resolve()),
      shutdown: vi.fn(() => Promise.resolve()),
    },
  }))
  vi.doMock("./agent-profile-service", () => ({
    agentProfileService: { syncAgentProfilesToACPRegistry: vi.fn() },
  }))
  vi.doMock("./skills-service", () => ({
    initializeBundledSkills: vi.fn(() => ({ copied: [], skipped: [] })),
    skillsService: {},
    startSkillsFolderWatcher: vi.fn(),
  }))
  vi.doMock("./cloudflare-tunnel", () => ({
    startCloudflareTunnel: vi.fn(),
    startNamedCloudflareTunnel: vi.fn(),
    checkCloudflaredInstalled: vi.fn(() => Promise.resolve(false)),
  }))
  vi.doMock("./models-dev-service", () => ({ initModelsDevService: vi.fn() }))
  vi.doMock("./loop-service", () => ({
    loopService: { startAllLoops: vi.fn(), stopAllLoops: vi.fn() },
  }))
  vi.doMock("./state", () => ({ setHeadlessMode: vi.fn(), state }))
  vi.doMock("./bundle-service", () => ({ findHubBundleHandoffFilePath }))
  vi.doMock("./hub-install", () => ({
    findHubBundleInstallBundleUrl,
    downloadHubBundleToTempFile,
  }))
  vi.doMock("./updater", () => ({ init: vi.fn() }))

  await import("./index")
  await flushPromises()

  return {
    handlers,
    createMainWindow,
    showMainWindow,
    getTextInputPanelHideActivationSuppressionDebugState,
    shouldSuppressMainWindowActivationForRecentTextInputHide,
    windows,
    state,
    downloadHubBundleToTempFile,
    requestSingleInstanceLock,
    releaseSingleInstanceLock,
    stopListeningToKeyboardEvents,
    quit,
  }
}

afterEach(() => {
  process.argv = [...originalArgv]
  vi.restoreAllMocks()
  vi.resetModules()
})

describe("Hub install handoff routing", () => {
  it("opens settings/agents with installBundle when launched with a .dotagents argv path", async () => {
    const bundlePath = "/tmp/from-startup.dotagents"
    const { createMainWindow } = await loadIndexForHubInstall([
      "electron",
      bundlePath,
    ])

    expect(createMainWindow).toHaveBeenCalledWith({
      url: `/settings/agents?installBundle=${encodeURIComponent(bundlePath)}`,
    })
  })

  it("downloads startup Hub install deep links before opening settings/agents", async () => {
    const deepLink =
      "dotagents://install?bundle=https%3A%2F%2Fhub.dotagentsprotocol.com%2Fbundles%2Ffeatured-agent.dotagents"
    const { createMainWindow, downloadHubBundleToTempFile } =
      await loadIndexForHubInstall(["electron", deepLink])

    expect(downloadHubBundleToTempFile).toHaveBeenCalledWith(
      "https://hub.dotagentsprotocol.com/bundles/featured-agent.dotagents",
    )
    expect(createMainWindow).toHaveBeenCalledWith({
      url: `/settings/agents?installBundle=${encodeURIComponent("/tmp/downloaded-featured-agent.dotagents")}`,
    })
  })

  it("routes open-file and second-instance bundle handoffs into showMainWindow", async () => {
    const bundlePath = "/tmp/from-handoff.dotagents"
    const { handlers, showMainWindow } = await loadIndexForHubInstall([
      "electron",
    ])

    showMainWindow.mockClear()
    const openFileHandler = handlers.get("open-file")?.[0] as (
      event: { preventDefault: () => void },
      filePath: string,
    ) => void
    const secondInstanceHandler = handlers.get("second-instance")?.[0] as (
      event: unknown,
      commandLine: string[],
    ) => void

    const event = { preventDefault: vi.fn() }
    openFileHandler(event, bundlePath)
    expect(event.preventDefault).toHaveBeenCalled()
    expect(showMainWindow).toHaveBeenCalledWith(
      `/settings/agents?installBundle=${encodeURIComponent(bundlePath)}`,
    )

    showMainWindow.mockClear()
    secondInstanceHandler({}, ["DotAgents", bundlePath])
    expect(showMainWindow).toHaveBeenCalledWith(
      `/settings/agents?installBundle=${encodeURIComponent(bundlePath)}`,
    )
  })

  it("requests a single-instance lock during normal GUI startup", async () => {
    const { requestSingleInstanceLock } = await loadIndexForHubInstall([
      "electron",
    ])

    expect(requestSingleInstanceLock).toHaveBeenCalledTimes(1)
  })

  it("quits immediately when a second GUI instance fails to acquire the lock", async () => {
    const { quit, createMainWindow, requestSingleInstanceLock } =
      await loadIndexForHubInstall(["electron"], {}, false)

    expect(requestSingleInstanceLock).toHaveBeenCalledTimes(1)
    expect(quit).toHaveBeenCalledTimes(1)
    expect(createMainWindow).not.toHaveBeenCalled()
  })

  it("logs a diagnostic when dev startup fails to acquire the lock", async () => {
    const consoleError = vi.spyOn(console, "error").mockImplementation(() => {})
    const { quit, createMainWindow, requestSingleInstanceLock } =
      await loadIndexForHubInstall(["electron"], {}, false, {
        isPackaged: false,
      })

    expect(requestSingleInstanceLock).toHaveBeenCalledTimes(1)
    expect(consoleError).toHaveBeenCalledWith(
      expect.stringContaining("Another DotAgents instance is already running"),
    )
    expect(quit).toHaveBeenCalledTimes(1)
    expect(createMainWindow).not.toHaveBeenCalled()
  })

  it("stops the keyboard listener during before-quit cleanup", async () => {
    const { handlers, quit, stopListeningToKeyboardEvents } =
      await loadIndexForHubInstall(["electron"])

    const beforeQuitHandler = handlers.get("before-quit")?.[0] as (
      event: { preventDefault: () => void },
    ) => Promise<void>
    const event = { preventDefault: vi.fn() }

    await beforeQuitHandler(event)

    expect(event.preventDefault).toHaveBeenCalledTimes(1)
    expect(stopListeningToKeyboardEvents).toHaveBeenCalledTimes(1)
    expect(quit).toHaveBeenCalled()
  })

  it("downloads Hub install deep links received via open-url and routes them into showMainWindow", async () => {
    const deepLink =
      "dotagents://install?bundle=https%3A%2F%2Fhub.dotagentsprotocol.com%2Fbundles%2Ffeatured-agent.dotagents"
    const { handlers, showMainWindow, downloadHubBundleToTempFile } =
      await loadIndexForHubInstall(["electron"])

    showMainWindow.mockClear()
    const openUrlHandler = handlers.get("open-url")?.[0] as (
      event: { preventDefault: () => void },
      url: string,
    ) => void

    const event = { preventDefault: vi.fn() }
    openUrlHandler(event, deepLink)
    await flushPromises()

    expect(event.preventDefault).toHaveBeenCalled()
    expect(downloadHubBundleToTempFile).toHaveBeenCalledWith(
      "https://hub.dotagentsprotocol.com/bundles/featured-agent.dotagents",
    )
    expect(showMainWindow).toHaveBeenCalledWith(
      `/settings/agents?installBundle=${encodeURIComponent("/tmp/downloaded-featured-agent.dotagents")}`,
    )
  })

  it("reopens the main window when the app becomes active via the macOS app switcher", async () => {
    const { handlers, showMainWindow, windows } = await loadIndexForHubInstall(["electron"])
    const didBecomeActiveHandler = handlers.get("did-become-active")?.[0] as (() => void) | undefined

    expect(didBecomeActiveHandler).toBeTypeOf("function")

    windows.set("main", { id: "main" })
    showMainWindow.mockClear()

    didBecomeActiveHandler?.()

    expect(showMainWindow).toHaveBeenCalledTimes(1)
  })

  it("dedupes paired macOS activate pulses so the main window is only reopened once", async () => {
    vi.useFakeTimers()

    try {
      vi.setSystemTime(new Date("2026-03-20T12:00:00Z"))
      vi.spyOn(process, "platform", "get").mockReturnValue("darwin")

      const { handlers, showMainWindow, windows } = await loadIndexForHubInstall(["electron"])
      const activateHandler = handlers.get("activate")?.[0] as (() => void) | undefined
      const didBecomeActiveHandler = handlers.get("did-become-active")?.[0] as (() => void) | undefined

      windows.set("main", { id: "main" })
      showMainWindow.mockClear()

      activateHandler?.()
      didBecomeActiveHandler?.()

      expect(showMainWindow).toHaveBeenCalledTimes(1)
    } finally {
      vi.useRealTimers()
    }
  })

  it("does not reopen the main window when the text-input panel is already active", async () => {
    const { handlers, showMainWindow, windows, state } = await loadIndexForHubInstall(["electron"])
    const didBecomeActiveHandler = handlers.get("did-become-active")?.[0] as (() => void) | undefined

    expect(didBecomeActiveHandler).toBeTypeOf("function")

    windows.set("main", { id: "main" })
    windows.set("panel", { isVisible: vi.fn(() => true) })
    state.isTextInputActive = true
    showMainWindow.mockClear()

    didBecomeActiveHandler?.()

    expect(showMainWindow).not.toHaveBeenCalled()
  })

  it("does not reopen the main window when text input is opening before the panel reports visible", async () => {
    const { handlers, showMainWindow, windows, state } = await loadIndexForHubInstall(["electron"])
    const didBecomeActiveHandler = handlers.get("did-become-active")?.[0] as (() => void) | undefined

    expect(didBecomeActiveHandler).toBeTypeOf("function")

    windows.set("main", { id: "main" })
    windows.set("panel", { isVisible: vi.fn(() => false) })
    state.isTextInputActive = true
    showMainWindow.mockClear()

    didBecomeActiveHandler?.()

    expect(showMainWindow).not.toHaveBeenCalled()
  })

  it("does not reopen the main window when the text-input panel was just intentionally hidden", async () => {
    const {
      handlers,
      showMainWindow,
      shouldSuppressMainWindowActivationForRecentTextInputHide,
      windows,
      state,
    } = await loadIndexForHubInstall(["electron"])
    const didBecomeActiveHandler = handlers.get("did-become-active")?.[0] as (() => void) | undefined

    expect(didBecomeActiveHandler).toBeTypeOf("function")

    windows.set("main", { id: "main" })
    windows.set("panel", { isVisible: vi.fn(() => false) })
    state.isTextInputActive = false
    shouldSuppressMainWindowActivationForRecentTextInputHide.mockReturnValue(true)
    showMainWindow.mockClear()

    didBecomeActiveHandler?.()

    expect(showMainWindow).not.toHaveBeenCalled()
  })
})
