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

describe('CurrencyPicker - Integration', () => {
  describe('Form Integration', () => {
    async function formFixture(): Promise<{ form: HTMLFormElement; picker: CurrencyPicker }> {
      const form = (await fixture(html`
        <form>
          <currency-picker name="currencies" label="Select Currencies" .selected="${['USD']}"></currency-picker>
          <button type="submit">Submit</button>
        </form>
      `)) as HTMLFormElement
      const picker = form.querySelector('currency-picker') as CurrencyPicker
      return { form, picker }
    }

    async function multiFormFixture(): Promise<{
      form: HTMLFormElement
      picker1: CurrencyPicker
      picker2: CurrencyPicker
    }> {
      const form = (await fixture(html`
        <form>
          <currency-picker name="baseCurrency" label="Base Currency" .selected="${['USD']}"></currency-picker>
          <currency-picker
            name="targetCurrencies"
            label="Target Currencies"
            multi
            .selected="${['EUR', 'GBP']}"
          ></currency-picker>
          <button type="submit">Submit</button>
        </form>
      `)) as HTMLFormElement
      const picker1 = form.querySelector('currency-picker[name="baseCurrency"]') as CurrencyPicker
      const picker2 = form.querySelector('currency-picker[name="targetCurrencies"]') as CurrencyPicker
      return { form, picker1, picker2 }
    }

    it('should submit form data correctly', async () => {
      const { form } = await formFixture()

      const formData = new FormData(form)
      assert.equal(formData.get('currencies'), 'USD')
    })

    it('should update form data when selection changes', async () => {
      const { form, picker } = await formFixture()

      // Enable multi-select for this test
      picker.multi = true
      picker.selected = ['EUR', 'GBP']
      await picker.updateComplete

      const formData = new FormData(form)
      assert.equal(formData.get('currencies'), 'EUR,GBP')
    })

    it('should handle form validation', async () => {
      const { form, picker } = await formFixture()
      picker.required = true

      // Clear selection to trigger validation
      picker.selected = []
      await picker.updateComplete

      const isValid = form.checkValidity()
      assert.isFalse(isValid)
    })

    it('should reset with form reset', async () => {
      const { form, picker } = await formFixture()

      picker.selected = ['EUR', 'GBP']
      await picker.updateComplete

      form.reset()
      await picker.updateComplete

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

    it('should handle multiple pickers in one form', async () => {
      const { form } = await multiFormFixture()

      const formData = new FormData(form)
      assert.equal(formData.get('baseCurrency'), 'USD')
      assert.equal(formData.get('targetCurrencies'), 'EUR,GBP')
    })

    it('should maintain independent state for multiple pickers', async () => {
      const { form, picker1, picker2 } = await multiFormFixture()

      picker1.selected = ['JPY']
      picker2.selected = ['CAD', 'AUD']
      await Promise.all([picker1.updateComplete, picker2.updateComplete])

      const formData = new FormData(form)
      assert.equal(formData.get('baseCurrency'), 'JPY')
      assert.equal(formData.get('targetCurrencies'), 'CAD,AUD')
    })

    it('should prevent a form from submitting', async () => {
      const { form, picker } = await formFixture()

      // Set required first, then clear selection to trigger validation
      picker.required = true
      await picker.updateComplete

      picker.selected = ['USD'] // Set a value first
      await picker.updateComplete
      picker.selected = [] // Then clear to trigger validation
      await picker.updateComplete

      // Check that error state is set (indicating validation failed)
      const isInvalid = !picker.validity.valid
      const selectElement = picker.shadowRoot?.querySelector('md-outlined-select')
      const hasAriaInvalid = selectElement?.getAttribute('aria-invalid') === 'true'

      assert.isTrue(isInvalid, 'Component should be in invalid state when validation fails')
      assert.isTrue(hasAriaInvalid, 'Select element should have aria-invalid=true when validation fails')

      const spy = sinon.spy()
      form.addEventListener('submit', spy)
      const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement
      submitButton.click()

      assert.isFalse(spy.called)
    })
  })

  describe('Real-world Usage Scenarios', () => {
    async function ecommerceFixture(): Promise<{
      container: HTMLElement
      baseCurrencyPicker: CurrencyPicker
      displayCurrenciesPicker: CurrencyPicker
    }> {
      const container = (await fixture(html`
        <div class="ecommerce-settings">
          <h2>Currency Settings</h2>
          <currency-picker
            name="baseCurrency"
            label="Base Currency"
            required
            .allowedCurrencies="${['USD', 'EUR', 'GBP']}"
          ></currency-picker>

          <currency-picker
            name="displayCurrencies"
            label="Display Currencies"
            multi
            required
            .allowedCurrencies="${['USD', 'EUR', 'GBP', 'JPY', 'CAD']}"
          ></currency-picker>
        </div>
      `)) as HTMLElement

      const baseCurrencyPicker = container.querySelector('[name="baseCurrency"]') as CurrencyPicker
      const displayCurrenciesPicker = container.querySelector('[name="displayCurrencies"]') as CurrencyPicker

      return { container, baseCurrencyPicker, displayCurrenciesPicker }
    }

    async function tradingFixture(): Promise<{
      container: HTMLElement
      fromPicker: CurrencyPicker
      toPicker: CurrencyPicker
    }> {
      const container = (await fixture(html`
        <div class="currency-exchange">
          <h2>Currency Exchange</h2>
          <currency-picker name="fromCurrency" label="From Currency" required .selected="${['USD']}"></currency-picker>

          <currency-picker name="toCurrency" label="To Currency" required .selected="${['EUR']}"></currency-picker>
        </div>
      `)) as HTMLElement

      const fromPicker = container.querySelector('[name="fromCurrency"]') as CurrencyPicker
      const toPicker = container.querySelector('[name="toCurrency"]') as CurrencyPicker

      return { container, fromPicker, toPicker }
    }

    it('should handle ecommerce currency settings scenario', async () => {
      const { baseCurrencyPicker, displayCurrenciesPicker } = await ecommerceFixture()

      // Set up typical ecommerce configuration
      baseCurrencyPicker.selected = ['USD']
      displayCurrenciesPicker.selected = ['USD', 'EUR', 'GBP']

      await Promise.all([baseCurrencyPicker.updateComplete, displayCurrenciesPicker.updateComplete])

      // Verify configuration
      assert.deepEqual(baseCurrencyPicker.selected, ['USD'])
      assert.deepEqual(displayCurrenciesPicker.selected, ['USD', 'EUR', 'GBP'])

      // Verify base currency is included in display currencies
      assert.include(displayCurrenciesPicker.selected, baseCurrencyPicker.selected[0])
    })

    it('should handle currency trading pair scenario', async () => {
      const { fromPicker, toPicker } = await tradingFixture()

      // Simulate changing trading pair
      fromPicker.selected = ['GBP']
      toPicker.selected = ['JPY']

      await Promise.all([fromPicker.updateComplete, toPicker.updateComplete])

      // Verify the pair is set correctly
      assert.deepEqual(fromPicker.selected, ['GBP'])
      assert.deepEqual(toPicker.selected, ['JPY'])

      // Verify they have different selections
      assert.notDeepEqual(fromPicker.selected, toPicker.selected)
    })

    it('should handle dynamic allowed currencies based on business rules', async () => {
      const { baseCurrencyPicker, displayCurrenciesPicker } = await ecommerceFixture()

      // Simulate business rule: display currencies depend on base currency
      baseCurrencyPicker.selected = ['EUR']
      await baseCurrencyPicker.updateComplete

      // Update allowed display currencies based on base currency
      if (baseCurrencyPicker.selected[0] === 'EUR') {
        displayCurrenciesPicker.allowedCurrencies = ['EUR', 'GBP', 'USD']
      }

      await displayCurrenciesPicker.updateComplete

      // Verify the business rule is applied
      assert.deepEqual(displayCurrenciesPicker.allowedCurrencies, ['EUR', 'GBP', 'USD'])
    })
  })

  describe('Error Recovery Scenarios', () => {
    async function errorRecoveryFixture(): Promise<CurrencyPicker> {
      return fixture(html` <currency-picker required showErrors label="Test Currency Picker"></currency-picker> `)
    }

    it('should recover from validation errors', async () => {
      const picker = await errorRecoveryFixture()

      // Trigger validation error
      picker.selected = []
      await picker.updateComplete

      // Verify error state
      assert.isTrue(!picker.validity.valid, 'Component should be in invalid state')

      // Recover by selecting a currency
      picker.selected = ['USD']
      await picker.updateComplete

      // Verify recovery
      assert.isTrue(picker.validity.valid, 'Component should be in valid state after recovery')
    })

    it('should handle invalid data gracefully', async () => {
      const picker = await errorRecoveryFixture()

      // Set invalid currency codes
      picker.selected = ['INVALID1', 'INVALID2', 'USD']
      await picker.updateComplete

      // Should filter out invalid codes and keep valid ones
      assert.include(picker.selected, 'USD')
      assert.notInclude(picker.selected, 'INVALID1')
      assert.notInclude(picker.selected, 'INVALID2')
    })

    it('should maintain functionality after error recovery', async () => {
      const picker = await errorRecoveryFixture()
      picker.multi = true

      // Cause and recover from error
      picker.selected = ['INVALID']
      await picker.updateComplete

      picker.selected = ['USD']
      await picker.updateComplete

      // Should still be able to add more currencies
      picker.selected = ['USD', 'EUR']
      await picker.updateComplete

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

  describe('Event Coordination', () => {
    async function coordinatedPickersFixture(): Promise<{
      container: HTMLElement
      picker1: CurrencyPicker
      picker2: CurrencyPicker
    }> {
      const container = (await fixture(html`
        <div class="coordinated-pickers">
          <currency-picker id="picker1" label="Source Currency" .selected="${['USD']}"></currency-picker>

          <currency-picker id="picker2" label="Target Currency" .selected="${['EUR']}"></currency-picker>
        </div>
      `)) as HTMLElement

      const picker1 = container.querySelector('#picker1') as CurrencyPicker
      const picker2 = container.querySelector('#picker2') as CurrencyPicker

      return { container, picker1, picker2 }
    }

    it('should coordinate between multiple pickers', async () => {
      const { picker1, picker2 } = await coordinatedPickersFixture()

      // Set up coordination: when picker1 changes, update picker2's allowed currencies
      picker1.addEventListener('change', () => {
        const selected = picker1.selected[0]
        if (selected) {
          // Don't allow same currency in both pickers
          const allCurrencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD']
          picker2.allowedCurrencies = allCurrencies.filter((c) => c !== selected)
        }
      })

      // Change picker1 selection
      picker1.selected = ['GBP']
      await picker1.updateComplete

      // Verify picker2 is updated
      assert.notInclude(picker2.allowedCurrencies, 'GBP')
    })

    it('should handle cascading updates without infinite loops', async () => {
      const { picker1, picker2 } = await coordinatedPickersFixture()

      let updateCount = 0
      const maxUpdates = 10

      const handleUpdate = () => {
        updateCount++
        if (updateCount < maxUpdates) {
          // Simulate some coordination logic
          if (picker1.selected[0] === picker2.selected[0]) {
            // Avoid same selection
            picker2.selected = picker2.selected[0] === 'USD' ? ['EUR'] : ['USD']
          }
        }
      }

      picker1.addEventListener('change', handleUpdate)
      picker2.addEventListener('change', handleUpdate)

      // Trigger initial change
      picker1.selected = ['EUR'] // Same as picker2's initial selection
      await picker1.updateComplete

      // Should not cause infinite loop
      assert.isBelow(updateCount, maxUpdates)
    })
  })

  describe('Dynamic Behavior', () => {
    async function dynamicFixture(): Promise<CurrencyPicker> {
      return fixture(html`<currency-picker></currency-picker>`)
    }

    it('should handle dynamic property changes', async () => {
      const picker = await dynamicFixture()

      // Start with basic configuration
      picker.label = 'Initial Label'
      picker.selected = ['USD']
      await picker.updateComplete

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

      // Verify multi-select is working
      const chips = picker.shadowRoot?.querySelectorAll('ui-chip')
      assert.equal(chips?.length, 2)

      // Dynamically change to required
      picker.required = true
      picker.selected = []
      await picker.updateComplete

      // Should show error
      assert.isTrue(!picker.validity.valid, 'Component should be in invalid state when required and empty')
    })

    it('should handle dynamic allowed currencies', async () => {
      const picker = await dynamicFixture()
      picker.multi = true // Enable multi-select for this test

      // Start with limited currencies
      picker.allowedCurrencies = ['USD', 'EUR']
      picker.selected = ['USD']
      await picker.updateComplete

      // Expand allowed currencies
      picker.allowedCurrencies = ['USD', 'EUR', 'GBP', 'JPY']
      await picker.updateComplete

      // Should now allow selection of new currencies
      picker.selected = ['USD', 'GBP']
      await picker.updateComplete

      assert.include(picker.selected, 'GBP')

      // Restrict currencies again
      picker.allowedCurrencies = ['USD', 'EUR']
      await picker.updateComplete

      // Should filter out disallowed currencies
      assert.notInclude(picker.selected, 'GBP')
    })
  })

  describe('Accessibility Integration', () => {
    async function accessibilityFixture(): Promise<{
      form: HTMLFormElement
      picker: CurrencyPicker
      submitButton: HTMLButtonElement
    }> {
      const form = (await fixture(html`
        <form>
          <label for="currency-picker">Select your preferred currency:</label>
          <currency-picker
            id="currency-picker"
            name="currency"
            required
            aria-describedby="currency-help"
          ></currency-picker>
          <div id="currency-help">Choose the currency for your account.</div>
          <button type="submit">Save Settings</button>
        </form>
      `)) as HTMLFormElement

      const picker = form.querySelector('currency-picker') as CurrencyPicker
      const submitButton = form.querySelector('button[type="submit"]') as HTMLButtonElement

      return { form, picker, submitButton }
    }

    it('should integrate properly with form labels and descriptions', async () => {
      const { picker } = await accessibilityFixture()

      // Verify ARIA relationships
      assert.equal(picker.id, 'currency-picker')
      assert.equal(picker.getAttribute('aria-describedby'), 'currency-help')
    })

    it('should announce validation errors appropriately', async () => {
      const { picker } = await accessibilityFixture()

      picker.showErrors = true
      picker.selected = [] // Trigger required validation
      await picker.updateComplete

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

    it('should maintain focus appropriately during interactions', async () => {
      const { picker } = await accessibilityFixture()

      const select = picker.shadowRoot?.querySelector('md-outlined-select') as HTMLElement
      select.focus()

      // Simulate selection
      picker.selected = ['USD']
      await picker.updateComplete

      // Focus should still be manageable
      assert.exists(select)
    })
  })
})
