import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'
import { vi } from 'vitest'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './consent'

expect.extend(toHaveNoViolations)

export interface ConsentTestConfig extends BaseTestConfig {
  devMode?: boolean
  googleAnalyticsId?: string
  hotjarId?: string
  cookieDomain?: string
  cookieSecure?: string // Using string to match the component's string attributes
  cookieExpiryDays?: string
  triggerType?: string
  triggerText?: string
  i18nLanguage?: string
}

// Use shared framework
export const createConsentTest = async (config: ConsentTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-consent'>,
    ConsentTestConfig
  >('pkt-consent', config)

  return {
    container,
    consent: element,
  }
}

// Cleanup after each test
afterEach(() => {
  document.body.innerHTML = ''
  // Clean up any injected scripts
  const existingScript = document.querySelector('#oslo-consent-script')
  if (existingScript) {
    existingScript.remove()
  }
  const existingStyles = document.querySelector('#oslo-consent-styles')
  if (existingStyles) {
    existingStyles.remove()
  }
  // Clean up global variables
  delete window.cookieBanner_googleAnalyticsId
  ;(window as any).cookieBanner_hotjarId = undefined
  ;(window as any).cookieBanner_devMode = undefined
  ;(window as any).cookieBanner_cookieDomain = undefined
  ;(window as any).cookieBanner_cookieSecure = undefined
  ;(window as any).cookieBanner_cookieExpiryDays = undefined
  ;(window as any).cookieBanner = undefined
  ;(window as any).__cookieEvents = undefined
})

describe('PktConsent', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { consent } = await createConsentTest()

      expect(consent).toBeInTheDocument()

      await consent.updateComplete
      expect(consent).toBeTruthy()
    })

    test('renders with default trigger type as button', async () => {
      const { consent } = await createConsentTest()

      await consent.updateComplete

      expect(consent.triggerType).toBe('button')

      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()
    })

    test('renders with custom trigger text', async () => {
      const customText = 'Custom consent settings'
      const { consent } = await createConsentTest({
        triggerText: customText,
      })

      await consent.updateComplete

      expect(consent.triggerText).toBe(customText)
      const button = consent.querySelector('pkt-button')
      expect(button?.textContent?.trim()).toBe(customText)
    })
  })

  describe('Properties and attributes', () => {
    test('applies default properties correctly', async () => {
      const { consent } = await createConsentTest()

      await consent.updateComplete

      expect(consent.devMode).toBe(false)
      expect(consent.triggerType).toBe('button')
      expect(consent.i18nLanguage).toBe('nb')
      expect(consent.hotjarId).toBe(null)
      expect(consent.googleAnalyticsId).toBe(null)
      expect(consent.cookieDomain).toBe(null)
      expect(consent.cookieSecure).toBe(null)
      expect(consent.cookieExpiryDays).toBe(null)
    })

    test('sets dev mode correctly', async () => {
      const { consent } = await createConsentTest({
        devMode: true,
      })

      await consent.updateComplete

      expect(consent.devMode).toBe(true)
    })

    test('sets analytics IDs correctly', async () => {
      const googleId = 'GA-123456789'
      const hotjarId = 'HJ-987654321'
      const { consent } = await createConsentTest({
        googleAnalyticsId: googleId,
        hotjarId: hotjarId,
      })

      await consent.updateComplete

      expect(consent.googleAnalyticsId).toBe(googleId)
      expect(consent.hotjarId).toBe(hotjarId)
    })

    test('sets cookie configuration correctly', async () => {
      const domain = '.example.com'
      const secure = 'true'
      const expiryDays = '365'
      const { consent } = await createConsentTest({
        cookieDomain: domain,
        cookieSecure: secure,
        cookieExpiryDays: expiryDays,
      })

      await consent.updateComplete

      expect(consent.cookieDomain).toBe(domain)
      expect(consent.cookieSecure).toBe(secure)
      expect(consent.cookieExpiryDays).toBe(expiryDays)
    })

    test('sets language correctly', async () => {
      const { consent } = await createConsentTest({
        i18nLanguage: 'en',
      })

      await consent.updateComplete

      expect(consent.i18nLanguage).toBe('en')
    })
  })

  describe('Trigger types', () => {
    test('renders as button trigger type', async () => {
      const { consent } = await createConsentTest({
        triggerType: 'button',
      })

      await consent.updateComplete

      expect(consent.triggerType).toBe('button')
      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()
      expect(button?.getAttribute('skin')).toBe(null) // Default button
    })

    test('renders as link trigger type', async () => {
      const { consent } = await createConsentTest({
        triggerType: 'link',
      })

      expect(consent.triggerType).toBe('link')
      const link = consent.querySelector('a.pkt-link')
      expect(link).toBeInTheDocument()
      expect(link?.getAttribute('href')).toBe('#')
    })

    test('renders as footer link trigger type', async () => {
      const { consent } = await createConsentTest({
        triggerType: 'footerlink',
      })

      expect(consent.triggerType).toBe('footerlink')
      const footerLink = consent.querySelector('a.pkt-footer__link')
      expect(footerLink).toBeInTheDocument()

      const icon = footerLink?.querySelector('pkt-icon')
      expect(icon).toBeInTheDocument()
      expect(icon?.getAttribute('name')).toBe('chevron-right')
    })

    test('renders as icon trigger type', async () => {
      const { consent } = await createConsentTest({
        triggerType: 'icon',
      })

      expect(consent.triggerType).toBe('icon')
      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()
      expect(button?.getAttribute('skin')).toBe('tertiary')
      expect(button?.getAttribute('variant')).toBe('icon-only')
      expect(button?.getAttribute('iconName')).toBe('cookie')
    })
  })

  describe('Event handling', () => {
    beforeEach(() => {
      // Mock the entire window.cookieBanner object
      Object.defineProperty(window, 'cookieBanner', {
        value: {
          cookieConsent: {
            validateConsentCookie: vi.fn().mockResolvedValue(true),
            getConsentCookie: vi.fn().mockReturnValue('mock-cookie'),
          },
          openCookieModal: vi.fn(),
        },
        writable: true,
      })

      // Mock window.__cookieEvents
      Object.defineProperty(window, '__cookieEvents', {
        value: {
          on: vi.fn(),
          off: vi.fn(),
        },
        writable: true,
      })

      // Use fake timers to control setTimeout
      vi.useFakeTimers()
    })

    afterEach(() => {
      // Clean up mocks and timers
      vi.useRealTimers()
      ;(window as any).cookieBanner = undefined
      ;(window as any).__cookieEvents = undefined
    })

    test('handles click event on button trigger', async () => {
      const { consent } = await createConsentTest()

      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()

      fireEvent.click(button!)

      // Fast-forward time to trigger setTimeout
      vi.runAllTimers()

      expect(window.cookieBanner.openCookieModal).toHaveBeenCalled()
    })

    test('handles click event on link trigger', async () => {
      const { consent } = await createConsentTest({
        triggerType: 'link',
      })

      const link = consent.querySelector('a.pkt-link')
      expect(link).toBeInTheDocument()

      fireEvent.click(link!)

      // Fast-forward time to trigger setTimeout
      vi.runAllTimers()

      expect(window.cookieBanner.openCookieModal).toHaveBeenCalled()
    })

    test('dispatches toggle-consent event', async () => {
      const { consent } = await createConsentTest()

      let eventFired = false
      let eventDetail: any = null

      consent.addEventListener('toggle-consent', (e: Event) => {
        eventFired = true
        eventDetail = (e as CustomEvent).detail
      })

      // Mock consent data
      const mockConsent = {
        value: JSON.stringify({
          items: [
            { name: 'analytics', consent: true },
            { name: 'marketing', consent: false },
          ],
        }),
      }

      consent.emitCookieConsents(mockConsent)

      expect(eventFired).toBe(true)
      expect(eventDetail).toEqual({
        analytics: true,
        marketing: false,
      })
    })
  })

  describe('Utility methods', () => {
    test('returnJsonOrObject handles JSON strings', async () => {
      const { consent } = await createConsentTest()

      const jsonString = '{"test": "value"}'
      const result = consent.returnJsonOrObject(jsonString)

      expect(result).toEqual({ test: 'value' })
    })

    test('returnJsonOrObject handles non-JSON objects', async () => {
      const { consent } = await createConsentTest()

      const nonJsonObject = { test: 'value' }
      const result = consent.returnJsonOrObject(nonJsonObject)

      expect(result).toEqual(nonJsonObject)
    })

    test('returnJsonOrObject handles invalid JSON gracefully', async () => {
      const { consent } = await createConsentTest()

      const invalidJson = 'invalid json string'
      const result = consent.returnJsonOrObject(invalidJson)

      expect(result).toBe(invalidJson)
    })
  })

  describe('Accessibility', () => {
    test('button trigger is accessible', async () => {
      const { container, consent } = await createConsentTest({
        triggerText: 'Cookie settings',
      })

      const results = await axe(container)
      expect(results).toHaveNoViolations()

      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()
      expect(button?.textContent?.trim()).toBe('Cookie settings')
    })

    test('link trigger is accessible', async () => {
      const { container, consent } = await createConsentTest({
        triggerType: 'link',
        triggerText: 'Cookie settings',
      })

      const results = await axe(container)
      expect(results).toHaveNoViolations()

      const link = consent.querySelector('a')
      expect(link).toBeInTheDocument()
      expect(link?.textContent?.trim()).toBe('Cookie settings')
    })

    test('footer link trigger is accessible', async () => {
      const { container, consent } = await createConsentTest({
        triggerType: 'footerlink',
        triggerText: 'Cookie settings',
      })

      const results = await axe(container)
      expect(results).toHaveNoViolations()

      const link = consent.querySelector('a.pkt-footer__link')
      expect(link).toBeInTheDocument()
      expect(link?.textContent?.trim()).toContain('Cookie settings')
    })

    test('icon trigger is accessible', async () => {
      const { container, consent } = await createConsentTest({
        triggerType: 'icon',
        triggerText: 'Cookie settings',
      })

      const results = await axe(container)
      expect(results).toHaveNoViolations()

      const button = consent.querySelector('pkt-button')
      expect(button).toBeInTheDocument()
      expect(button?.textContent?.trim()).toContain('Cookie settings')
    })
  })

  describe('Integration and lifecycle', () => {
    test('sets global variables on firstUpdated', async () => {
      const googleId = 'GA-123456789'
      const hotjarId = 'HJ-987654321'
      await createConsentTest({
        devMode: true,
        googleAnalyticsId: googleId,
        hotjarId: hotjarId,
        cookieDomain: '.test.com',
      })

      expect(window.cookieBanner_googleAnalyticsId).toBe(googleId)
      expect(window.cookieBanner_hotjarId).toBe(hotjarId)
      expect(window.cookieBanner_devMode).toBe(true)
      expect(window.cookieBanner_cookieDomain).toBe('.test.com')
    })

    test('cleans up event listeners on disconnect', async () => {
      const { consent } = await createConsentTest()

      // Mock the event system
      window.__cookieEvents = {
        on: vi.fn(),
        off: vi.fn(),
      }

      // Set up a handler
      const mockHandler = vi.fn()
      consent['_cookieEventHandler'] = mockHandler

      // Disconnect the component
      consent.disconnectedCallback()

      expect(window.__cookieEvents.off).toHaveBeenCalledWith('CookieManager.setCookie', mockHandler)
    })
  })
})
