import '@testing-library/jest-dom'
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './header-service'

// Nested components are imported by header-service itself, but we need to wait for them
const waitForNestedElements = async () => {
  await Promise.all([
    customElements.whenDefined('pkt-button'),
    customElements.whenDefined('pkt-icon'),
    customElements.whenDefined('pkt-link'),
    customElements.whenDefined('pkt-textinput'),
    customElements.whenDefined('pkt-header-user-menu'),
  ])
}

export interface HeaderServiceTestConfig extends BaseTestConfig {
  'service-name'?: string
  'service-link'?: string
  'logo-link'?: string
  compact?: boolean
  'hide-logo'?: boolean
  position?: 'fixed' | 'relative'
  'scroll-behavior'?: 'hide' | 'none'
  'show-search'?: boolean
  'search-placeholder'?: string
  'search-value'?: string
  'log-out-button-placement'?: 'userMenu' | 'header' | 'both' | 'none'
  'can-change-representation'?: boolean
  'opened-menu'?: 'none' | 'slot' | 'search' | 'user'
  'mobile-breakpoint'?: number
  'tablet-breakpoint'?: number
}

const createHeaderServiceTest = async (config: HeaderServiceTestConfig = {}) => {
  const result = await createElementTest<
    CustomElementFor<'pkt-header-service'>,
    HeaderServiceTestConfig
  >('pkt-header-service', config)
  await waitForNestedElements()
  await result.element.updateComplete
  return result
}

describe('pkt-header-service', () => {
  beforeEach(() => {
    // Mock window.matchMedia for responsive behavior
    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: vi.fn().mockImplementation((query) => ({
        matches: false,
        media: query,
        onchange: null,
        addListener: vi.fn(),
        removeListener: vi.fn(),
        addEventListener: vi.fn(),
        removeEventListener: vi.fn(),
        dispatchEvent: vi.fn(),
      })),
    })

    // Mock ResizeObserver
    global.ResizeObserver = class ResizeObserver {
      observe() {}
      unobserve() {}
      disconnect() {}
    }

    // Mock scrollTo
    window.scrollTo = vi.fn()
  })

  afterEach(() => {
    document.body.innerHTML = ''
  })

  describe('basic rendering', () => {
    it('renders with service name', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })
      const serviceName = element.querySelector('.pkt-header-service__service-name')
      expect(serviceName).toBeTruthy()
      expect(serviceName?.textContent).toContain('My Service')
    })

    it('applies compact class when compact attribute is set', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc', compact: true })
      const header = element.querySelector('.pkt-header-service')
      expect(header?.classList.contains('pkt-header-service--compact')).toBe(true)
    })

    it('shows logo by default', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      const logo = element.querySelector('.pkt-header-service__logo')
      expect(logo).toBeTruthy()
    })

    it('hides logo when hide-logo attribute is set', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'hide-logo': true,
      })
      const logo = element.querySelector('.pkt-header-service__logo')
      expect(logo).toBeNull()
    })
  })

  describe('logoLink attribute', () => {
    it('renders logo as link when logo-link is provided', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'logo-link': 'https://oslo.kommune.no',
      })
      const logoLink = element.querySelector('.pkt-header-service__logo a')
      expect(logoLink).toBeTruthy()
      expect(logoLink?.getAttribute('href')).toBe('https://oslo.kommune.no')
    })

    it('dispatches logo-click event when logo button is clicked', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })

      const eventSpy = vi.fn()
      element.addEventListener('logo-click', eventSpy)

      const logoButton = element.querySelector('.pkt-header-service__logo button')

      if (logoButton) {
        logoButton.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }))
        expect(eventSpy).toHaveBeenCalled()
      }
    })

    it('renders logo without link when logo-link is not provided', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      const logoLink = element.querySelector('.pkt-header-service__logo a')
      expect(logoLink).toBeNull()
    })
  })

  describe('serviceLink and service-click event', () => {
    it('renders service name as link when service-link is provided', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'My Service',
        'service-link': 'https://example.com',
      })
      // Check that serviceLink property is set correctly
      expect(element.serviceLink).toBe('https://example.com')
      // Check that pkt-link element exists (may not fully render in test env)
      const linkElement = element.querySelector('pkt-link')
      expect(linkElement).toBeTruthy()
    })

    it('dispatches service-click event when service button is clicked', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })

      const eventSpy = vi.fn()
      element.addEventListener('service-click', eventSpy)

      const serviceButton = element.querySelector('button.pkt-header-service__service-link')

      if (serviceButton) {
        serviceButton.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }))
        expect(eventSpy).toHaveBeenCalled()
      }
    })

    it('renders service name as span when service-link is not provided', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })
      const span = element.querySelector('span.pkt-header-service__service-link')
      expect(span).toBeTruthy()
    })
  })

  describe('position and scrollBehavior attributes', () => {
    it('applies fixed class by default (position="fixed")', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      const header = element.querySelector('.pkt-header-service')
      expect(header?.classList.contains('pkt-header-service--fixed')).toBe(true)
    })

    it('does not apply fixed class when position="relative"', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        position: 'relative',
      })
      const header = element.querySelector('.pkt-header-service')
      expect(header?.classList.contains('pkt-header-service--fixed')).toBe(false)
    })

    it('applies scroll-to-hide class by default (scroll-behavior="hide")', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      const header = element.querySelector('.pkt-header-service')
      expect(header?.classList.contains('pkt-header-service--scroll-to-hide')).toBe(true)
    })

    it('does not apply scroll-to-hide class when scroll-behavior="none"', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'scroll-behavior': 'none',
      })
      const header = element.querySelector('.pkt-header-service')
      expect(header?.classList.contains('pkt-header-service--scroll-to-hide')).toBe(false)
    })
  })

  describe('search functionality', () => {
    it('does not render search input by default', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      const search = element.querySelector('.pkt-header-service__search-input')
      expect(search).toBeNull()
    })

    it('renders search container when show-search is true', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'show-search': true,
      })
      const searchContainer = element.querySelector('.pkt-header-service__search-container')
      expect(searchContainer).toBeTruthy()
    })

    it('dispatches search event when Enter is pressed in search input', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'show-search': true,
      })

      const eventSpy = vi.fn()
      element.addEventListener('search', eventSpy)

      const searchInput = element.querySelector(
        '.pkt-header-service__search-input input',
      ) as HTMLInputElement
      if (searchInput) {
        searchInput.value = 'test query'
        searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
        await element.updateComplete
        expect(eventSpy).toHaveBeenCalled()
      }
    })

    it('dispatches search-change event when search input value changes', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'show-search': true,
      })

      const eventSpy = vi.fn()
      element.addEventListener('search-change', eventSpy)

      const searchInput = element.querySelector(
        '.pkt-header-service__search-input input',
      ) as HTMLInputElement
      if (searchInput) {
        searchInput.value = 'test'
        searchInput.dispatchEvent(new Event('input', { bubbles: true }))
        await element.updateComplete
        expect(eventSpy).toHaveBeenCalled()
      }
    })
  })

  describe('user menu', () => {
    it('renders user container when user is provided', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      element.user = { name: 'Aksel Olsen' }
      await element.updateComplete

      // Check that user container exists
      const userContainer = element.querySelector('.pkt-header-service__user-container')
      expect(userContainer).toBeTruthy()
      // Check that user property is passed correctly
      expect(element.user.name).toBe('Aksel Olsen')
    })

    it('passes user data to user menu component', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      element.user = { name: 'Aksel Olsen' }
      await element.updateComplete

      // Verify user property is set
      expect(element.user).toEqual({ name: 'Aksel Olsen' })
    })

    it('passes representing data to user menu component', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      element.user = { name: 'Aksel' }
      element.representing = { name: 'Oslo Kommune' }
      await element.updateComplete

      // Verify representing property is set
      expect(element.representing).toEqual({ name: 'Oslo Kommune' })
    })

    it('passes canChangeRepresentation prop when set', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'can-change-representation': true,
      })
      element.user = { name: 'Aksel' }
      element.representing = { name: 'Oslo Kommune' }
      await element.updateComplete

      // Verify canChangeRepresentation property is set
      expect(element.canChangeRepresentation).toBe(true)
    })
  })

  describe('logout button placement', () => {
    it('shows logout button in user area when log-out-button-placement="header"', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'log-out-button-placement': 'header',
      })
      element.user = { name: 'Aksel' }
      await element.updateComplete

      const userArea = element.querySelector('.pkt-header-service__user')
      const logoutBtn = userArea?.querySelector('pkt-button')
      expect(logoutBtn).toBeTruthy()
    })

    it('sets up logout button with correct properties', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'log-out-button-placement': 'header',
      })
      element.user = { name: 'Aksel' }
      await element.updateComplete

      // Verify logOutButtonPlacement property is set
      expect(element.logOutButtonPlacement).toBe('header')
      // Verify logout button component exists in DOM
      const logoutBtn = element.querySelector('.pkt-header-service__user pkt-button')
      expect(logoutBtn).toBeTruthy()
    })

    it('does not show logout button in header areas when log-out-button-placement="userMenu"', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'log-out-button-placement': 'userMenu',
      })
      element.user = { name: 'Aksel' }
      await element.updateComplete

      // Logout should not be in header areas
      const userArea = element.querySelector('.pkt-header-service__user')
      const contentArea = element.querySelector('.pkt-header-service__content')
      const userAreaLogout = userArea?.querySelectorAll('pkt-button[icon-name="exit"]')
      const contentAreaLogout = contentArea?.querySelectorAll('pkt-button[icon-name="exit"]')

      expect(userAreaLogout?.length || 0).toBe(0)
      expect(contentAreaLogout?.length || 0).toBe(0)
    })
  })

  describe('user menu items', () => {
    it('passes user menu items to user menu component', async () => {
      const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
      element.user = { name: 'Aksel' }
      element.userMenu = [
        { title: 'Mine bookinger', iconName: 'heart', target: '/bookinger' },
        { title: 'Innstillinger', iconName: 'cogwheel', target: () => {} },
      ]
      await element.updateComplete

      // Verify userMenu property is set correctly
      expect(element.userMenu).toHaveLength(2)
      expect(element.userMenu[0].title).toBe('Mine bookinger')
      expect(element.userMenu[1].title).toBe('Innstillinger')
    })
  })

  describe('events', () => {
    it('dispatches change-representation event when change button is clicked', async () => {
      const { element } = await createHeaderServiceTest({
        'service-name': 'Svc',
        'can-change-representation': true,
      })
      element.user = { name: 'Aksel' }
      element.representing = { name: 'Oslo Kommune' }
      await element.updateComplete

      const eventSpy = vi.fn()
      element.addEventListener('change-representation', eventSpy)

      // Open user menu and click change button
      const userButton = element.querySelector('.pkt-user-menu__button') as HTMLElement
      userButton?.click()
      await element.updateComplete

      const allButtons = element.querySelectorAll('button')
      const changeButton = Array.from(allButtons || []).find((btn) =>
        btn.textContent?.includes('Endre organisasjon'),
      )

      if (changeButton) {
        changeButton.click()
        await element.updateComplete
        expect(eventSpy).toHaveBeenCalled()
      }
    })
  })
})
