import { describe, it, expect, beforeEach, vi } from 'vitest'
import './test-utils'
import {
  NO_PERMISSIONS_REQUIRED,
  PERMISSION_NAMES,
  getPermissionStatus,
  updatePermission,
  removePermissions,
  showNotification,
  getPosition
} from './common'

describe('common.js', () => {
  beforeEach(() => {
    browser.storage.local._reset()
    browser.notifications._reset()
    vi.clearAllMocks()
  })

  describe('NO_PERMISSIONS_REQUIRED', () => {
    it('should include replaceURL without permission', () => {
      expect(NO_PERMISSIONS_REQUIRED.replaceURL).toBe(true)
    })

    it('should include peekPublicKey without permission', () => {
      expect(NO_PERMISSIONS_REQUIRED.peekPublicKey).toBe(true)
    })

    it('should not include getPublicKey without permission', () => {
      expect(NO_PERMISSIONS_REQUIRED.getPublicKey).toBeUndefined()
    })

    it('should not include signEvent without permission', () => {
      expect(NO_PERMISSIONS_REQUIRED.signEvent).toBeUndefined()
    })
  })

  describe('PERMISSION_NAMES', () => {
    it('should have permission descriptions for all operations', () => {
      expect(PERMISSION_NAMES.getPublicKey).toBe('read your public key')
      expect(PERMISSION_NAMES.signEvent).toBe(
        'sign events using your private key'
      )
      expect(PERMISSION_NAMES['nip04.encrypt']).toBe(
        'encrypt messages to peers'
      )
      expect(PERMISSION_NAMES['nip04.decrypt']).toBe(
        'decrypt messages from peers'
      )
      expect(PERMISSION_NAMES['nip44.encrypt']).toBe(
        'encrypt messages to peers'
      )
      expect(PERMISSION_NAMES['nip44.decrypt']).toBe(
        'decrypt messages from peers'
      )
    })
  })

  describe('getPermissionStatus', () => {
    it('should return undefined when no policies exist', async () => {
      const result = await getPermissionStatus('example.com', 'getPublicKey')
      expect(result).toBeUndefined()
    })

    it('should return true when host has accepted permission', async () => {
      browser.storage.local.set({
        policies: {
          'example.com': {
            true: {
              getPublicKey: {
                conditions: {},
                created_at: 1234567890
              }
            }
          }
        }
      })

      const result = await getPermissionStatus('example.com', 'getPublicKey')
      expect(result).toBe(true)
    })

    it('should return false when host has rejected permission', async () => {
      browser.storage.local.set({
        policies: {
          'example.com': {
            false: {
              getPublicKey: {
                conditions: {},
                created_at: 1234567890
              }
            }
          }
        }
      })

      const result = await getPermissionStatus('example.com', 'getPublicKey')
      expect(result).toBe(false)
    })

    it('should return true over false when both exist', async () => {
      browser.storage.local.set({
        policies: {
          'example.com': {
            true: {
              getPublicKey: { conditions: {}, created_at: 1234567890 }
            },
            false: {
              getPublicKey: { conditions: {}, created_at: 1234567890 }
            }
          }
        }
      })

      const result = await getPermissionStatus('example.com', 'getPublicKey')
      expect(result).toBe(true)
    })

    it('should check kind conditions for signEvent', async () => {
      browser.storage.local.set({
        policies: {
          'example.com': {
            true: {
              signEvent: {
                conditions: { kinds: { 1: true, 4: true } },
                created_at: 1234567890
              }
            }
          }
        }
      })

      const event1 = { kind: 1 }
      const event4 = { kind: 4 }
      const event7 = { kind: 7 }

      expect(
        await getPermissionStatus('example.com', 'signEvent', event1)
      ).toBe(true)
      expect(
        await getPermissionStatus('example.com', 'signEvent', event4)
      ).toBe(true)
      expect(
        await getPermissionStatus('example.com', 'signEvent', event7)
      ).toBeUndefined()
    })
  })

  describe('updatePermission', () => {
    it('should create new permission', async () => {
      await updatePermission('example.com', 'getPublicKey', true, {})

      const { policies } = await browser.storage.local.get('policies')
      expect(policies['example.com'].true.getPublicKey).toBeDefined()
      expect(policies['example.com'].true.getPublicKey.conditions).toEqual({})
    })

    it('should update existing permission', async () => {
      await updatePermission('example.com', 'getPublicKey', true, {})
      await updatePermission('example.com', 'getPublicKey', true, {})

      const { policies } = await browser.storage.local.get('policies')
      expect(policies['example.com'].true.getPublicKey).toBeDefined()
    })

    it('should remove reverse policy when same conditions', async () => {
      await updatePermission('example.com', 'getPublicKey', false, {})
      await updatePermission('example.com', 'getPublicKey', true, {})

      const { policies } = await browser.storage.local.get('policies')
      expect(policies['example.com']?.false?.getPublicKey).toBeUndefined()
      expect(policies['example.com'].true.getPublicKey).toBeDefined()
    })
  })

  describe('removePermissions', () => {
    it('should remove specific permission', async () => {
      browser.storage.local.set({
        policies: {
          'example.com': {
            true: {
              getPublicKey: { conditions: {}, created_at: 1234567890 }
            }
          }
        }
      })

      await removePermissions('example.com', true, 'getPublicKey')

      const { policies } = await browser.storage.local.get('policies')
      expect(policies['example.com'].true.getPublicKey).toBeUndefined()
    })
  })

  describe('showNotification', () => {
    it('should create notification when enabled', async () => {
      browser.storage.local.set({ notifications: true })

      await showNotification('example.com', true, 'getPublicKey', {})

      expect(browser.notifications.create).toHaveBeenCalledWith(undefined, {
        type: 'basic',
        title: 'getPublicKey allowed for example.com',
        message: expect.any(String),
        iconUrl: 'icons/48x48.png'
      })
    })

    it('should not create notification when disabled', async () => {
      browser.storage.local.set({ notifications: false })

      await showNotification('example.com', true, 'getPublicKey', {})

      expect(browser.notifications.create).not.toHaveBeenCalled()
    })

    it('should format event details in message', async () => {
      browser.storage.local.set({ notifications: true })

      await showNotification('example.com', true, 'signEvent', {
        event: { kind: 1, content: 'Hello', tags: [] }
      })

      expect(browser.notifications.create).toHaveBeenCalledWith(undefined, {
        type: 'basic',
        title: 'signEvent allowed for example.com',
        message: expect.stringContaining('1'),
        iconUrl: 'icons/48x48.png'
      })
    })
  })

  describe('getPosition', () => {
    it('should return centered position', async () => {
      const position = await getPosition(440, 420)

      expect(position.top).toBe(430) // Math.round(100 + (1080 - 420) / 2)
      expect(position.left).toBe(840) // Math.round(100 + (1920 - 440) / 2)
    })

    it('should handle window without position data', async () => {
      browser.windows.getLastFocused.mockResolvedValueOnce({
        top: undefined,
        left: undefined
      })

      const position = await getPosition(440, 420)

      expect(position.top).toBe(0)
      expect(position.left).toBe(0)
    })
  })
})
