import { beforeEach, describe, expect, it, vi } from 'vitest'
import {
  forcePwaRecovery,
  markReloadAttempt,
  recentlyAttemptedReload,
  reconcileServiceWorkerOnStartup,
  reconcileVersionOnStartup
} from './pwaStartup.js'

const STALE_RECOVERY_COUNT_KEY = 'pwa_stale_recovery_count'
const STALE_RECOVERY_LAST_KEY = 'pwa_stale_recovery_last_ts'

describe('pwaStartup reload guards', () => {
  beforeEach(() => {
    sessionStorage.clear()
  })

  it('blocks repeated reload attempts within the debounce window', () => {
    expect(recentlyAttemptedReload(10_000)).toBe(false)
    markReloadAttempt(10_000)
    expect(recentlyAttemptedReload(12_000)).toBe(true)
    expect(recentlyAttemptedReload(15_000)).toBe(false)
  })
})

describe('forcePwaRecovery stale counter reset', () => {
  beforeEach(() => {
    sessionStorage.clear()
    vi.unstubAllEnvs()
    vi.restoreAllMocks()
  })

  it('clears stale recovery counter before hard recovery reload', async () => {
    vi.stubEnv('DEV', false)
    sessionStorage.setItem(STALE_RECOVERY_COUNT_KEY, '2')
    sessionStorage.setItem(STALE_RECOVERY_LAST_KEY, String(Date.now()))

    const reload = vi.fn()
    vi.stubGlobal('location', { reload })
    vi.stubGlobal('caches', {
      keys: vi.fn().mockResolvedValue([]),
      delete: vi.fn()
    })
    Object.defineProperty(navigator, 'serviceWorker', {
      configurable: true,
      value: {
        getRegistrations: vi.fn().mockResolvedValue([])
      }
    })

    await forcePwaRecovery()

    expect(sessionStorage.getItem(STALE_RECOVERY_COUNT_KEY)).toBeNull()
    expect(sessionStorage.getItem(STALE_RECOVERY_LAST_KEY)).toBeNull()
    expect(reload).toHaveBeenCalledOnce()
  })

  it('returns false when hard recovery was just attempted', async () => {
    sessionStorage.setItem('pwa_hard_recovery_ts', String(Date.now()))
    const result = await forcePwaRecovery()
    expect(result).toBe(false)
  })
})

describe('reconcileServiceWorkerOnStartup', () => {
  beforeEach(() => {
    sessionStorage.clear()
    vi.unstubAllEnvs()
  })

  it('returns false in dev mode', async () => {
    vi.stubEnv('DEV', true)
    await expect(reconcileServiceWorkerOnStartup()).resolves.toBe(false)
  })

  it('returns false when no waiting worker exists', async () => {
    vi.stubEnv('DEV', false)
    Object.defineProperty(navigator, 'serviceWorker', {
      configurable: true,
      value: {
        controller: {},
        getRegistration: vi.fn().mockResolvedValue({
          waiting: null,
          installing: null,
          update: vi.fn().mockResolvedValue(undefined),
          addEventListener: vi.fn()
        }),
        addEventListener: vi.fn()
      }
    })

    await expect(reconcileServiceWorkerOnStartup()).resolves.toBe(false)
  })

  it('returns false when waiting worker activation never takes over', async () => {
    vi.useFakeTimers()
    const postMessage = vi.fn()
    const addEventListener = vi.fn()
    vi.stubEnv('DEV', false)

    Object.defineProperty(navigator, 'serviceWorker', {
      configurable: true,
      value: {
        controller: { scriptURL: '/sw.js?v=1' },
        getRegistration: vi.fn().mockResolvedValue({
          waiting: { postMessage },
          installing: null,
          update: vi.fn().mockResolvedValue(undefined),
          addEventListener: vi.fn()
        }),
        addEventListener
      }
    })

    const reconcilePromise = reconcileServiceWorkerOnStartup()
    await vi.advanceTimersByTimeAsync(4_000)

    await expect(reconcilePromise).resolves.toBe(false)
    expect(postMessage).toHaveBeenCalledWith({ type: 'SKIP_WAITING' })
    vi.useRealTimers()
  })
})

describe('reconcileVersionOnStartup', () => {
  beforeEach(() => {
    sessionStorage.clear()
    vi.unstubAllEnvs()
    vi.restoreAllMocks()
  })

  it('returns noop in dev mode', async () => {
    vi.stubEnv('DEV', true)
    await expect(reconcileVersionOnStartup()).resolves.toBe('noop')
  })

  it('returns noop when deployed version matches bundled version', async () => {
    vi.stubEnv('DEV', false)
    vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({ version: '0.1.0.57' })
    }))
    vi.stubGlobal('__APP_VERSION__', '0.1.0.57')

    await expect(reconcileVersionOnStartup()).resolves.toBe('noop')
  })
})
