import { fixture, assert, html } from '@open-wc/testing'
import CurrencyPicker from '../../../src/elements/currency/internals/Picker.js'
import '../../../src/elements/currency/currency-picker.js'

describe('CurrencyPicker - Core Tests', () => {
  async function basicFixture(): Promise<CurrencyPicker> {
    return fixture(html`<currency-picker></currency-picker>`)
  }

  async function multiSelectFixture(): Promise<CurrencyPicker> {
    return fixture(html`<currency-picker multi></currency-picker>`)
  }

  async function allowedCurrenciesFixture(): Promise<CurrencyPicker> {
    return fixture(html`<currency-picker .allowedCurrencies="${['USD', 'EUR', 'GBP']}"></currency-picker>`)
  }

  async function requiredFixture(): Promise<CurrencyPicker> {
    return fixture(html`<currency-picker required></currency-picker>`)
  }

  describe('Basic Functionality', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await basicFixture()
    })

    it('should create element', () => {
      assert.instanceOf(picker, CurrencyPicker)
    })

    it('should have default properties', () => {
      assert.deepEqual(picker.selected, [])
      assert.deepEqual(picker.allowedCurrencies, [])
      assert.equal(picker.label, 'Add Currency')
      assert.isFalse(picker.multi)
      assert.isFalse(picker.required)
      assert.isFalse(picker.disabled)
      assert.isTrue(picker.showErrors)
    })

    it('should render currency selector', () => {
      const selector = picker.shadowRoot?.querySelector('md-outlined-select')
      assert.exists(selector)
      assert.equal(selector?.getAttribute('label'), 'Add Currency')
    })

    it('should populate currency options', async () => {
      await picker.updateComplete
      const options = picker.shadowRoot?.querySelectorAll('md-select-option')
      assert.exists(options)
      assert.isAtLeast(options?.length || 0, 25) // Should have popular currencies
    })
  })

  describe('Currency Selection', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await basicFixture()
    })

    it('should select a currency', async () => {
      picker.selected = ['USD']
      await picker.updateComplete
      assert.deepEqual(picker.selected, ['USD'])
    })

    it('should get selected currencies', async () => {
      // Test single-select mode (default)
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

      const currencies = picker.getSelectedCurrencies()
      assert.equal(currencies.length, 1, 'Should only allow one selection in single-select mode')
      assert.equal(currencies[0].code, 'USD')

      // Test multi-select mode
      picker.multi = true
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

      const currenciesMulti = picker.getSelectedCurrencies()
      assert.equal(currenciesMulti.length, 2)
      assert.equal(currenciesMulti[0].code, 'USD')
      assert.equal(currenciesMulti[1].code, 'EUR')
    })

    it('should clear selection', async () => {
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

      picker.clearSelection()
      await picker.updateComplete

      assert.deepEqual(picker.selected, [])
    })
  })

  describe('Multi-select Mode', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await multiSelectFixture()
    })

    it('should allow multiple selections', async () => {
      picker.selected = ['USD', 'EUR', 'GBP']
      await picker.updateComplete

      assert.equal(picker.selected.length, 3)
      assert.include(picker.selected, 'USD')
      assert.include(picker.selected, 'EUR')
      assert.include(picker.selected, 'GBP')
    })

    it('should render chips for selected currencies', async () => {
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

      const chipSet = picker.shadowRoot?.querySelector('ui-chip-set')
      const chips = picker.shadowRoot?.querySelectorAll('ui-chip')

      assert.exists(chipSet)
      assert.equal(chips?.length, 2)
    })
  })

  describe('Allowed Currencies', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await allowedCurrenciesFixture()
    })

    it('should filter options based on allowed currencies', async () => {
      await picker.updateComplete

      const options = picker.shadowRoot?.querySelectorAll('md-select-option[value]')
      const optionValues = Array.from(options || [])
        .map((option) => option.getAttribute('value'))
        .filter(Boolean)

      // Should only have USD, EUR, GBP (plus empty option)
      assert.isAtMost(optionValues.length, 3)
      assert.include(optionValues, 'USD')
      assert.include(optionValues, 'EUR')
      assert.include(optionValues, 'GBP')
    })
  })

  describe('Validation and Error Handling', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await requiredFixture()
    })

    it('should show error state in UI when showErrors is true', async () => {
      picker.showErrors = true

      // First set a value, then clear it to trigger validation
      picker.selected = ['USD']
      await picker.updateComplete

      picker.selected = [] // This should trigger required validation error
      await picker.updateComplete

      const errorElement = picker.shadowRoot?.querySelector('.error')
      const selectElement = picker.shadowRoot?.querySelector('md-outlined-select')
      const isInvalid = !picker.validity.valid
      const hasAriaInvalid = selectElement?.getAttribute('aria-invalid') === 'true'

      assert.exists(errorElement, 'Error element should be displayed')
      assert.isTrue(isInvalid, 'Component should be in invalid state')
      assert.isTrue(hasAriaInvalid, 'Select element should have aria-invalid=true')
      assert.isTrue(picker.matches(':invalid'), 'Has the :invalid pseudo-class')
    })

    it('should not show error UI when showErrors is false', async () => {
      picker.showErrors = false
      picker.selected = [] // Directly set to empty to trigger required validation error
      await picker.updateComplete

      const errorElement = picker.shadowRoot?.querySelector('.error')
      assert.isNull(errorElement)
    })

    it('should validate invalid currency codes', async () => {
      picker.selected = ['INVALID_CODE']
      await picker.updateComplete

      // Should filter out invalid codes
      assert.notInclude(picker.selected, 'INVALID_CODE')
    })

    it('should prevent multiple selections in single-select mode', async () => {
      picker.multi = false
      picker.selected = ['USD', 'EUR'] // Multiple selections in single-select mode
      await picker.updateComplete

      // Should only keep one selection when multi=false
      assert.equal(picker.selected.length, 1)
      assert.equal(picker.selected[0], 'USD') // Should keep the first one
    })
  })

  describe('Form Integration', () => {
    it('should be form-associated', () => {
      assert.isTrue(CurrencyPicker.formAssociated)
    })

    it('should handle form state restoration', async () => {
      // Test single-select mode (default)
      const pickerSingle = await basicFixture()
      pickerSingle.formStateRestoreCallback('USD,EUR')
      await pickerSingle.updateComplete

      assert.deepEqual(pickerSingle.selected, ['USD'], 'Single-select should only restore first value')

      // Test multi-select mode
      const pickerMulti = await multiSelectFixture()
      pickerMulti.formStateRestoreCallback('USD,EUR')
      await pickerMulti.updateComplete

      assert.deepEqual(pickerMulti.selected, ['USD', 'EUR'], 'Multi-select should restore all values')
    })

    it('should handle form reset', async () => {
      const picker = await basicFixture()
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

      picker.formResetCallback()
      await picker.updateComplete

      assert.deepEqual(picker.selected, [])
    })
  })

  describe('Accessibility', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await basicFixture()
    })

    it('should have proper ARIA attributes', async () => {
      await picker.updateComplete

      const container = picker.shadowRoot?.querySelector('.currency-picker')
      const select = picker.shadowRoot?.querySelector('md-outlined-select')

      assert.equal(container?.getAttribute('role'), 'group')
      assert.exists(container?.getAttribute('aria-label'))
      assert.exists(select?.getAttribute('aria-invalid'))
    })

    it('should update ARIA label based on selection', async () => {
      picker.selected = ['USD']
      await picker.updateComplete

      const container = picker.shadowRoot?.querySelector('.currency-picker')
      const ariaLabel = container?.getAttribute('aria-label')

      assert.include(ariaLabel || '', 'USD selected')
    })

    it('should have error announcements', async () => {
      picker.showErrors = true
      picker.required = true
      picker.clearSelection()
      await picker.updateComplete

      const errorElement = picker.shadowRoot?.querySelector('.error')
      assert.equal(errorElement?.getAttribute('role'), 'alert')
      assert.equal(errorElement?.getAttribute('aria-live'), 'polite')
    })
  })

  describe('Edge Cases', () => {
    let picker: CurrencyPicker

    beforeEach(async () => {
      picker = await basicFixture()
    })

    it('should handle empty arrays gracefully', async () => {
      picker.selected = []
      picker.allowedCurrencies = []
      await picker.updateComplete

      assert.deepEqual(picker.selected, [])
      assert.deepEqual(picker.getSelectedCurrencies(), [])
    })

    it('should handle disabled state', async () => {
      picker.disabled = true
      await picker.updateComplete

      const select = picker.shadowRoot?.querySelector('md-outlined-select')
      assert.isTrue(select?.hasAttribute('disabled'))
    })

    it('should handle disconnection/reconnection', async () => {
      picker.selected = ['USD']
      await picker.updateComplete

      // Disconnect and reconnect
      picker.remove()
      document.body.appendChild(picker)
      await picker.updateComplete

      assert.deepEqual(picker.selected, ['USD'])
    })
  })
})
