import { fixture, assert, nextFrame, html, aTimeout } from '@open-wc/testing'
import { ApiDefinitions, AmfDefinitions } from '@api-client/core/browser.js'
import { AmfLoader } from '../AmfLoader.js'
import { DomEventsAmfStore } from '../../../src/amf/store/DomEventsAmfStore.js'
import ApiRequestDocumentElement from '../../../src/elements/amf/ApiRequestDocument.element.js'
import '../../../src/define/amf/api-request-document.js'

describe('ApiRequestDocumentElement', () => {
  const loader = new AmfLoader()
  const store = new DomEventsAmfStore(window)
  store.listen()

  async function basicFixture(
    shape?: ApiDefinitions.IApiRequest,
    mimeType?: string
  ): Promise<ApiRequestDocumentElement> {
    const element = (await fixture(
      html`<api-request-document
        .queryDebouncerTimeout="${0}"
        .request="${shape}"
        .mimeType="${mimeType}"
      ></api-request-document>`
    )) as ApiRequestDocumentElement
    await aTimeout(1)
    return element
  }

  describe('request headers', () => {
    let model: AmfDefinitions.IAmfDocument
    before(async () => {
      model = await loader.getGraph()
      store.amf = model
    })

    it('renders the request headers', async () => {
      const data = loader.getRequest(model, '/people', 'get')
      const element = await basicFixture(data)

      assert.isTrue(element.hasHeaders, 'hasHeaders is true')

      const section = element.shadowRoot!.querySelector('.params-section [data-ctrl-property="headersOpened"]')
      assert.ok(section, 'the section is rendered')

      const params = element.shadowRoot!.querySelectorAll('api-parameter-document[data-name="header"]')
      assert.lengthOf(params, 2, 'has all parameters document')
    })

    it('ignores headers section when no headers', async () => {
      const data = loader.getRequest(model, '/messages', 'get')
      const element = await basicFixture(data)

      assert.isFalse(element.hasHeaders, 'hasHeaders is true')

      const section = element.shadowRoot!.querySelector('.params-section [data-ctrl-property="headersOpened"]')
      assert.notOk(section, 'the section is not rendered')
    })
  })

  describe('payload rendering', () => {
    let model: AmfDefinitions.IAmfDocument
    before(async () => {
      model = await loader.getGraph()
      store.amf = model
    })

    it('renders the payload schema', async () => {
      const data = loader.getRequest(model, '/people', 'post')
      const element = await basicFixture(data)

      const node = element.shadowRoot!.querySelector('api-payload-document')
      assert.ok(node, 'has the payload document')
    })

    it('ignores the payload when not defined', async () => {
      const data = loader.getRequest(model, '/people', 'get')
      const element = await basicFixture(data)

      const node = element.shadowRoot!.querySelector('api-payload-document')
      assert.notOk(node)
    })
  })

  describe('request media type selector', () => {
    let model: AmfDefinitions.IAmfDocument
    before(async () => {
      model = await loader.getGraph()
      store.amf = model
    })

    it('renders the request media type selector', async () => {
      const data = loader.getRequest(model, '/people', 'put')
      const element = await basicFixture(data)

      const section = element.shadowRoot!.querySelector('.params-section[data-controlled-by="payloadOpened"]')!
      const selector = section.querySelector('.media-type-selector')!
      assert.ok(selector, 'has the payload document')

      const buttons = selector.querySelectorAll('ui-radio')
      assert.lengthOf(buttons, 2, 'has 2 media types to select')
    })

    it('ignores the media type when not defined', async () => {
      const data = loader.getRequest(model, '/orgs/{orgId}', 'put')
      const element = await basicFixture(data)

      const section = element.shadowRoot!.querySelector('.params-section[data-controlled-by="payloadOpened"]')!
      const selector = section.querySelector('.media-type-selector')
      assert.notOk(selector, 'has the payload document')
    })

    it('selecting a mime type changes the payload', async () => {
      const data = loader.getRequest(model, '/people', 'put')
      const element = await basicFixture(data)

      const section = element.shadowRoot!.querySelector('.params-section[data-controlled-by="payloadOpened"]')!
      const selector = section.querySelector('.media-type-selector')!

      const buttons = selector.querySelectorAll('ui-radio')

      buttons[1].click()

      await nextFrame()

      assert.equal(element.mimeType, 'application/xml', 'media type is changed')
    })
  })

  describe('APIC-463: request media type selector', () => {
    let model: AmfDefinitions.IAmfDocument
    before(async () => {
      model = await loader.getGraph('APIC-463')
      store.amf = model
    })

    it('renders the request media type selector', async () => {
      const data = loader.getRequest(model, '/test', 'post')
      const element = await basicFixture(data)

      const section = element.shadowRoot!.querySelector('.params-section[data-controlled-by="payloadOpened"]')!
      const selector = section.querySelector('.media-type-selector')!
      assert.ok(selector, 'has the payload document')

      const buttons = selector.querySelectorAll('ui-radio')
      assert.lengthOf(buttons, 2, 'has 2 media types to select')

      assert.equal(buttons[0].value, 'binary/octet-stream')
      assert.equal(buttons[1].value, 'multipart/form-data')
    })
  })

  describe('APIC-561: anyOf', () => {
    let model: AmfDefinitions.IAmfDocument
    before(async () => {
      model = await loader.getGraph('anyOf')
      store.amf = model
    })

    it('renders the documentation for anyOf type', async () => {
      const data = loader.getRequest(model, 'test', 'publish')
      const element = await basicFixture(data)
      const doc = element.shadowRoot!.querySelector('api-payload-document')
      assert.ok(doc)
    })
  })
})
