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

const { exitImmediatelyMock } = vi.hoisted(() => ({
  exitImmediatelyMock: vi.fn(),
}))

vi.mock('../src/dimsdk/cli/exit-controller.ts', () => ({
  exitImmediately: exitImmediatelyMock,
}))

const {
  queueUpgradeToast,
  withTimeout,
} = await import('../src/dimsdk/cli/runtime.ts')

const realProcess = globalThis.process

function setProcess(value: NodeJS.Process): void {
  Object.defineProperty(globalThis, 'process', {
    configurable: true,
    value,
  })
}

afterEach(() => {
  delete (globalThis as typeof globalThis & {
    __DIMCODE_UPGRADE_TOAST__?: (text: string) => void
  }).__DIMCODE_UPGRADE_TOAST__
  exitImmediatelyMock.mockReset()
  setProcess(realProcess)
})

describe('cli runtime helpers', () => {
  it('routes upgrade toasts to the in-memory handler before env fallback', () => {
    const handler = vi.fn()
    const proc = {
      ...realProcess,
      env: {},
    } as NodeJS.Process
    setProcess(proc)
    ;(globalThis as typeof globalThis & {
      __DIMCODE_UPGRADE_TOAST__?: (text: string) => void
    }).__DIMCODE_UPGRADE_TOAST__ = handler

    queueUpgradeToast('  New version  ')

    expect(handler).toHaveBeenCalledWith('New version')
    expect(proc.env.DIMCODE_STARTUP_UPGRADE_TOAST).toBeUndefined()
  })

  it('stores startup upgrade toasts in process env when no handler exists', () => {
    const proc = {
      ...realProcess,
      env: {},
    } as NodeJS.Process
    setProcess(proc)

    queueUpgradeToast('Update installed')
    queueUpgradeToast('Another message')

    expect(proc.env.DIMCODE_STARTUP_UPGRADE_TOAST).toBe('Update installed')
  })

  it('times out unresolved promises to null', async () => {
    await expect(withTimeout(new Promise(() => {}), 5)).resolves.toBeNull()
    await expect(withTimeout(Promise.resolve('ok'), 50)).resolves.toBe('ok')
  })
})
