import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import { type IPktLoader } from './loader'
import './loader'

export interface LoaderTestConfig extends Partial<IPktLoader>, BaseTestConfig {}

// Use shared framework
export const createLoaderTest = async (config: LoaderTestConfig = {}) => {
  const { container, element } = await createElementTest<
    CustomElementFor<'pkt-loader'>,
    LoaderTestConfig
  >('pkt-loader', { ...config, isLoading: undefined })

  // Set boolean properties directly on the element after creation
  if (config.isLoading !== undefined) {
    element.isLoading = config.isLoading
    await element.updateComplete
  }
  if (config.inline !== undefined) {
    element.inline = config.inline
    await element.updateComplete
  }

  return {
    container,
    loader: element,
  }
}

expect.extend(toHaveNoViolations)

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

describe('PktLoader', () => {
  describe('Rendering and basic functionality', () => {
    test('renders without errors', async () => {
      const { loader } = await createLoaderTest()

      expect(loader).toBeInTheDocument()
      expect(loader).toBeTruthy()
    })

    test('renders loading state by default', async () => {
      const { loader } = await createLoaderTest()

      expect(loader.isLoading).toBe(true)
      const spinner = loader.querySelector('.pkt-loader__spinner')
      expect(spinner).toBeInTheDocument()
    })

    test('renders content when not loading', async () => {
      const { loader } = await createLoaderTest({
        content: '<p>Content</p>',
        isLoading: false,
      })

      expect(loader.isLoading).toBe(false)
      const content = loader.querySelector('.pkt-contents')
      expect(content).toBeInTheDocument()
      expect(content?.classList.contains('pkt-hide')).toBe(false)
    })

    test('renders with slot content', async () => {
      const { loader } = await createLoaderTest({
        content: '<p>Test content</p>',
      })

      const content = loader.querySelector('p')
      expect(content).toBeInTheDocument()
      expect(content?.textContent).toBe('Test content')
    })
  })

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

      expect(loader.isLoading).toBe(true)
      expect(loader.inline).toBe(false)
      expect(loader.size).toBe('medium')
      expect(loader.variant).toBe('shapes')
      expect(loader.delay).toBe(0)
    })

    test('sets properties correctly', async () => {
      const { loader } = await createLoaderTest({
        size: 'small',
        variant: 'blue',
        delay: 100,
        isLoading: false,
        inline: true,
      })

      expect(loader.isLoading).toBe(false)
      expect(loader.inline).toBe(true)
      expect(loader.size).toBe('small')
      expect(loader.variant).toBe('blue')
      expect(loader.delay).toBe(100)
    })
  })

  describe('Size variants', () => {
    test('applies small size correctly', async () => {
      const { loader } = await createLoaderTest({ size: 'small' })

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.classList.contains('pkt-loader--small')).toBe(true)
    })

    test('applies medium size correctly', async () => {
      const { loader } = await createLoaderTest({ size: 'medium' })

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.classList.contains('pkt-loader--medium')).toBe(true)
    })

    test('applies large size correctly', async () => {
      const { loader } = await createLoaderTest({ size: 'large' })

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.classList.contains('pkt-loader--large')).toBe(true)
    })
  })

  describe('Loader variants', () => {
    test('applies shapes variant correctly', async () => {
      const { loader } = await createLoaderTest({ variant: 'shapes' })

      const icon = loader.querySelector('pkt-icon')
      expect(icon?.getAttribute('name')).toBe('loader')
    })

    test('applies blue variant correctly', async () => {
      const { loader } = await createLoaderTest({ variant: 'blue' })

      const icon = loader.querySelector('pkt-icon')
      expect(icon?.getAttribute('name')).toBe('spinner-blue')
    })

    test('applies rainbow variant correctly', async () => {
      const { loader } = await createLoaderTest({ variant: 'rainbow' })

      const icon = loader.querySelector('pkt-icon')
      expect(icon?.getAttribute('name')).toBe('spinner')
    })
  })

  describe('Inline mode', () => {
    test('applies box class when inline is false', async () => {
      const { loader } = await createLoaderTest({ inline: false })

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.classList.contains('pkt-loader--box')).toBe(true)
    })

    test('applies inline class when inline is true', async () => {
      const { loader } = await createLoaderTest({ inline: true })

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.classList.contains('pkt-loader--inline')).toBe(true)
    })
  })

  describe('Message functionality', () => {
    test('renders message when provided', async () => {
      const { loader } = await createLoaderTest({ message: 'Loading data...' })

      const message = loader.querySelector('p')
      expect(message).toBeInTheDocument()
      expect(message?.textContent).toBe('Loading data...')
    })

    test('does not render message when not provided', async () => {
      const { loader } = await createLoaderTest()

      const message = loader.querySelector('p')
      expect(message).not.toBeInTheDocument()
    })
  })

  describe('Delay functionality', () => {
    test('handles delay correctly', async () => {
      const { loader } = await createLoaderTest({ delay: 100 })

      // Initially should not show spinner due to delay
      let spinner = loader.querySelector('.pkt-loader__spinner')
      expect(spinner).not.toBeInTheDocument()

      // Wait for delay to pass
      await new Promise((resolve) => setTimeout(resolve, 150))
      await loader.updateComplete

      // Now spinner should be visible
      spinner = loader.querySelector('.pkt-loader__spinner')
      expect(spinner).toBeInTheDocument()
    }, 300)
  })

  describe('Content visibility', () => {
    test('hides content when loading', async () => {
      const { loader } = await createLoaderTest({
        isLoading: true,
        content: '<p>Content</p>',
      })

      const content = loader.querySelector('.pkt-contents')
      expect(content?.classList.contains('pkt-hide')).toBe(true)
    })

    test('shows content when not loading', async () => {
      const { loader } = await createLoaderTest({
        isLoading: false,
        content: '<p>Content</p>',
      })

      const content = loader.querySelector('.pkt-contents')
      expect(content?.classList.contains('pkt-hide')).toBe(false)
    })
  })

  describe('ARIA and accessibility', () => {
    test('has correct ARIA attributes', async () => {
      const { loader } = await createLoaderTest()

      const loaderDiv = loader.querySelector('.pkt-loader')
      expect(loaderDiv?.getAttribute('role')).toBe('status')
      expect(loaderDiv?.getAttribute('aria-live')).toBe('polite')
      // Check the aria-busy attribute
      expect(loaderDiv?.getAttribute('aria-busy')).toBe('true')
    })

    test('updates aria-busy when loading state changes', async () => {
      const { loader } = await createLoaderTest({ isLoading: false })

      const loaderDiv = loader.querySelector('.pkt-loader')
      // Check the aria-busy attribute
      expect(loaderDiv?.getAttribute('aria-busy')).toBe('false')
    })
  })

  describe('Accessibility', () => {
    test('loader is accessible', async () => {
      const { container } = await createLoaderTest({
        message: 'Loading content',
        content: '<p>Content</p>',
      })

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