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

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

vi.mock('electron', () => ({
  session: {
    fromPartition: sessionFromPartitionMock
  }
}))

vi.mock('./browser-manager', () => ({
  browserManager: {
    notifyPermissionDenied: vi.fn(),
    handleGuestWillDownload: vi.fn()
  }
}))

import { browserSessionRegistry } from './browser-session-registry'
import { ORCA_BROWSER_PARTITION } from '../../shared/constants'

describe('BrowserSessionRegistry', () => {
  beforeEach(() => {
    sessionFromPartitionMock.mockReset()
    sessionFromPartitionMock.mockReturnValue({
      setPermissionRequestHandler: vi.fn(),
      setPermissionCheckHandler: vi.fn(),
      setDisplayMediaRequestHandler: vi.fn(),
      on: vi.fn(),
      clearStorageData: vi.fn().mockResolvedValue(undefined),
      clearCache: vi.fn().mockResolvedValue(undefined)
    })
  })

  it('has a default profile on construction', () => {
    const defaultProfile = browserSessionRegistry.getDefaultProfile()
    expect(defaultProfile.id).toBe('default')
    expect(defaultProfile.scope).toBe('default')
    expect(defaultProfile.partition).toBe(ORCA_BROWSER_PARTITION)
  })

  it('allows the default partition', () => {
    expect(browserSessionRegistry.isAllowedPartition(ORCA_BROWSER_PARTITION)).toBe(true)
  })

  it('rejects unknown partitions', () => {
    expect(browserSessionRegistry.isAllowedPartition('persist:evil-partition')).toBe(false)
  })

  it('creates an isolated profile with a unique partition', () => {
    const profile = browserSessionRegistry.createProfile('isolated', 'Test Isolated')
    expect(profile.scope).toBe('isolated')
    expect(profile.partition).toMatch(/^persist:orca-browser-session-/)
    expect(profile.partition).not.toBe(ORCA_BROWSER_PARTITION)
    expect(profile.label).toBe('Test Isolated')
    expect(profile.source).toBeNull()
  })

  it('allows created profile partitions', () => {
    const profile = browserSessionRegistry.createProfile('isolated', 'Allowed')
    expect(browserSessionRegistry.isAllowedPartition(profile.partition)).toBe(true)
  })

  it('creates an imported profile', () => {
    const profile = browserSessionRegistry.createProfile('imported', 'My Import')
    expect(profile.scope).toBe('imported')
    expect(profile.partition).toMatch(/^persist:orca-browser-session-/)
  })

  it('resolves partition for a known profile', () => {
    const profile = browserSessionRegistry.createProfile('isolated', 'Resolve Test')
    expect(browserSessionRegistry.resolvePartition(profile.id)).toBe(profile.partition)
  })

  it('resolves default partition for null/undefined profileId', () => {
    expect(browserSessionRegistry.resolvePartition(null)).toBe(ORCA_BROWSER_PARTITION)
    expect(browserSessionRegistry.resolvePartition(undefined)).toBe(ORCA_BROWSER_PARTITION)
  })

  it('resolves default partition for unknown profileId', () => {
    expect(browserSessionRegistry.resolvePartition('nonexistent')).toBe(ORCA_BROWSER_PARTITION)
  })

  it('lists all profiles', () => {
    const before = browserSessionRegistry.listProfiles().length
    browserSessionRegistry.createProfile('isolated', 'List Test')
    const after = browserSessionRegistry.listProfiles()
    expect(after.length).toBe(before + 1)
  })

  it('updates profile source', () => {
    const profile = browserSessionRegistry.createProfile('imported', 'Source Test')
    const updated = browserSessionRegistry.updateProfileSource(profile.id, {
      browserFamily: 'edge',
      importedAt: Date.now()
    })
    expect(updated).not.toBeNull()
    expect(updated!.source?.browserFamily).toBe('edge')
  })

  it('deletes a non-default profile', async () => {
    const profile = browserSessionRegistry.createProfile('isolated', 'Delete Test')
    expect(browserSessionRegistry.isAllowedPartition(profile.partition)).toBe(true)
    const deleted = await browserSessionRegistry.deleteProfile(profile.id)
    expect(deleted).toBe(true)
    expect(browserSessionRegistry.isAllowedPartition(profile.partition)).toBe(false)
    expect(browserSessionRegistry.getProfile(profile.id)).toBeNull()
  })

  it('refuses to delete the default profile', async () => {
    const deleted = await browserSessionRegistry.deleteProfile('default')
    expect(deleted).toBe(false)
    expect(browserSessionRegistry.getDefaultProfile()).not.toBeNull()
  })

  it('hydrates profiles from persisted data', () => {
    const fakeProfile = {
      id: 'hydrate-test-id',
      scope: 'imported' as const,
      partition: 'persist:orca-browser-session-hydrate-test-id',
      label: 'Hydrated',
      source: { browserFamily: 'manual' as const, importedAt: 1000 }
    }
    browserSessionRegistry.hydrateFromPersisted([fakeProfile])
    expect(browserSessionRegistry.getProfile('hydrate-test-id')).not.toBeNull()
    expect(browserSessionRegistry.isAllowedPartition(fakeProfile.partition)).toBe(true)
  })

  it('sets up session policies for new partitions', () => {
    browserSessionRegistry.createProfile('isolated', 'Policy Test')
    expect(sessionFromPartitionMock).toHaveBeenCalled()
    const mockSession = sessionFromPartitionMock.mock.results[0]?.value
    expect(mockSession?.setPermissionRequestHandler).toHaveBeenCalled()
    expect(mockSession?.setPermissionCheckHandler).toHaveBeenCalled()
  })

  describe('setupClientHintsOverride', () => {
    it('overrides sec-ch-ua headers for Edge UA', () => {
      const onBeforeSendHeaders = vi.fn()
      const mockSess = { webRequest: { onBeforeSendHeaders } } as never
      const edgeUa =
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.6890.3 Safari/537.36 Edg/147.0.3210.5'

      browserSessionRegistry.setupClientHintsOverride(mockSess, edgeUa)

      expect(onBeforeSendHeaders).toHaveBeenCalledWith(
        { urls: ['https://*/*'] },
        expect.any(Function)
      )

      const callback = vi.fn()
      const listener = onBeforeSendHeaders.mock.calls[0][1]
      listener(
        { requestHeaders: { 'sec-ch-ua': 'old', 'sec-ch-ua-full-version-list': 'old' } },
        callback
      )
      const modified = callback.mock.calls[0][0].requestHeaders
      expect(modified['sec-ch-ua']).toContain('Microsoft Edge')
      expect(modified['sec-ch-ua']).toContain('"147"')
      expect(modified['sec-ch-ua-full-version-list']).toContain('147.0.3210.5')
    })

    it('overrides sec-ch-ua headers for Chrome UA', () => {
      const onBeforeSendHeaders = vi.fn()
      const mockSess = { webRequest: { onBeforeSendHeaders } } as never
      const chromeUa =
        'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.6890.3 Safari/537.36'

      browserSessionRegistry.setupClientHintsOverride(mockSess, chromeUa)

      const callback = vi.fn()
      const listener = onBeforeSendHeaders.mock.calls[0][1]
      listener({ requestHeaders: { 'sec-ch-ua': 'old' } }, callback)
      const modified = callback.mock.calls[0][0].requestHeaders
      expect(modified['sec-ch-ua']).toContain('Google Chrome')
      expect(modified['sec-ch-ua']).not.toContain('Microsoft Edge')
    })

    it('does not register handler for non-Chrome UA', () => {
      const onBeforeSendHeaders = vi.fn()
      const mockSess = { webRequest: { onBeforeSendHeaders } } as never

      browserSessionRegistry.setupClientHintsOverride(
        mockSess,
        'Mozilla/5.0 (compatible; MSIE 10.0)'
      )

      expect(onBeforeSendHeaders).not.toHaveBeenCalled()
    })

    it('leaves non-Client-Hints headers unchanged', () => {
      const onBeforeSendHeaders = vi.fn()
      const mockSess = { webRequest: { onBeforeSendHeaders } } as never
      browserSessionRegistry.setupClientHintsOverride(
        mockSess,
        'Mozilla/5.0 Chrome/147.0.0.0 Safari/537.36'
      )

      const callback = vi.fn()
      const listener = onBeforeSendHeaders.mock.calls[0][1]
      listener(
        { requestHeaders: { Cookie: 'abc=123', 'sec-ch-ua': 'old', Accept: 'text/html' } },
        callback
      )
      const modified = callback.mock.calls[0][0].requestHeaders
      expect(modified.Cookie).toBe('abc=123')
      expect(modified.Accept).toBe('text/html')
    })
  })
})
