import { fixture, assert, nextFrame, html, oneEvent } from '@open-wc/testing'
import sinon from 'sinon'
import type UiCollapse from '../../../src/md/collapse/internals/Collapse.js'
import '../../../src/md/collapse/ui-collapse.js'

describe('UiCollapse', () => {
  async function basicFixture(): Promise<UiCollapse> {
    return fixture(
      html`<ui-collapse open>
        <div style="height:100px;">Lorem ipsum</div>
      </ui-collapse>`
    )
  }

  async function emptyFixture(): Promise<UiCollapse> {
    return fixture(html`<ui-collapse open></ui-collapse>`)
  }

  async function horizontalFixture(): Promise<UiCollapse> {
    return fixture(
      html`<ui-collapse open horizontal>
        <div style="width:100px;">Lorem ipsum</div>
      </ui-collapse>`
    )
  }

  describe('constructor', () => {
    let collapse: UiCollapse

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

    it('open attribute', () => {
      assert.equal(collapse.open, true)
    })

    it('animated by default', () => {
      assert.notOk(collapse.noAnimation, '`noAnimation` is false')
    })

    it('not transitioning on attached', () => {
      assert.isFalse(collapse.transitioning, '`transitioning` is false')
    })

    it('horizontal attribute', () => {
      assert.equal(collapse.horizontal, false)
    })

    it('default opened height', () => {
      assert.equal(collapse.style.maxHeight, '')
    })
  })

  describe('#open', () => {
    let collapse: UiCollapse
    let collapseHeight: string

    beforeEach(async () => {
      collapse = await basicFixture()
      collapseHeight = getComputedStyle(collapse).height
    })

    it('set open to false triggers animation', async () => {
      collapse.open = false
      await collapse.updateComplete
      // Animation got enabled.
      assert.notEqual(collapse.style.transitionDuration, '0s')
      assert.equal(collapse.transitioning, true, 'transitioning became true')
      await oneEvent(collapse, 'transitionend')
      assert.equal(collapse.style.transitionDuration, '0s')
      assert.equal(collapse.transitioning, false, 'transitioning became false')
    })

    it('set open to false, then to true', async () => {
      // Trigger 1st toggle.
      collapse.open = false
      await oneEvent(collapse, 'transitionend')
      // Check if size is 0px.
      assert.equal(collapse.style.maxHeight, '0px')
      collapse.open = true
      await collapse.updateComplete
      assert.equal(collapse.style.maxHeight, collapseHeight)
      await oneEvent(collapse, 'transitionend')
      // Check finalSize after animation is done.
      assert.equal(collapse.style.height, '')
    })

    it('open change trigger resize', async () => {
      // No animations for faster test.
      collapse.noAnimation = true
      collapse.open = false
      await oneEvent(collapse, 'resize')
    })

    it('overflow is hidden while animating', async () => {
      assert.equal(getComputedStyle(collapse).overflow, 'visible')
      collapse.open = false
      await collapse.updateComplete
      assert.equal(getComputedStyle(collapse).overflow, 'hidden')
      await oneEvent(collapse, 'transitionend')
      // Should still be hidden.
      assert.equal(getComputedStyle(collapse).overflow, 'hidden')
    })
  })

  describe('#transitioning', () => {
    let collapse: UiCollapse

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

    it('updates only during transitions between open/close states', async () => {
      const spy = sinon.spy()

      collapse.addEventListener('transitioning', spy)
      collapse.noAnimation = true
      assert.equal(spy.callCount, 0, 'transitioning not affected by noAnimation')
      collapse.horizontal = true
      await collapse.updateComplete
      assert.equal(spy.callCount, 1, 'transitioning affected by horizontal')
      collapse.open = false
      await collapse.updateComplete
      assert.equal(spy.callCount, 3, 'transitioning changed 3 times')
      assert.equal(collapse.transitioning, false, 'transitioning is false')
    })
  })

  describe('#noAnimation', () => {
    let collapse: UiCollapse

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

    it('disables animations', async () => {
      collapse.noAnimation = true
      // trying to animate the size update
      collapse.open = false
      await collapse.updateComplete
      // Animation immediately disabled.
      assert.equal(collapse.style.maxHeight, '0px')
    })
  })

  describe('#horizontal', () => {
    let collapse: UiCollapse
    let collapseWidth: string

    beforeEach(async () => {
      collapse = await horizontalFixture()
      collapseWidth = getComputedStyle(collapse).width
    })

    it('opened attribute', () => {
      assert.equal(collapse.open, true)
    })

    it('horizontal attribute', () => {
      assert.equal(collapse.horizontal, true)
    })

    it('default opened width', () => {
      assert.equal(collapse.style.width, '')
    })

    it('set opened to false, then to true', async () => {
      // Trigger 1st toggle.
      collapse.open = false
      await collapse.updateComplete
      // Size should be immediately set.
      assert.equal(collapse.style.maxWidth, '0px', 'maxWidth is set')
      await oneEvent(collapse, 'transitionend')

      // Check if size is still 0px.
      assert.equal(collapse.style.maxWidth, '0px', 'maxWidth is still set')
      // Trigger 2nd toggle.
      collapse.open = true
      await collapse.updateComplete
      assert.equal(collapse.style.maxWidth, collapseWidth, 'maxWidth is set to the collapse width')

      await oneEvent(collapse, 'transitionend')
      assert.equal(collapse.style.width, '')
    })
  })

  describe('No content', () => {
    let collapse: UiCollapse

    beforeEach(async () => {
      collapse = await emptyFixture()
    })

    it('empty&opened shows dynamically loaded content', async () => {
      await nextFrame()
      collapse.toggle()
      collapse.toggle()
      assert.equal(collapse.style.maxHeight, '')
    })
  })

  describe('a11y', () => {
    async function a11yFixture(): Promise<UiCollapse> {
      return fixture(
        html`<ui-collapse id="collapse" tabindex="0">
          <div>
            Forma temperiemque cornua sidera dissociata cornua recessit innabilis ligavit: solidumque coeptis nullus
            caelum sponte phoebe di regat mentisque tanta austro capacius amphitrite sui quin postquam semina fossae
            liquidum umor galeae coeptis caligine liberioris quin liquidum matutinis invasit posset: flexi glomeravit
            radiis certis invasit oppida postquam onerosior inclusum dominari opifex terris pace finxit quam aquae nunc
            sine altae auroram quam habentem homo totidemque scythiam in pondus ensis tegit caecoque poena lapidosos
            humanas coeperunt poena aetas totidem nec natura aethera locavit caelumque distinxit animalibus phoebe
            cingebant moderantum porrexerat terrae possedit sua sole diu summaque obliquis melioris orbem
          </div>
        </ui-collapse>`
      )
    }

    let collapse: UiCollapse

    beforeEach(async () => {
      // Force focus on body at every test.
      document.body.focus()
      collapse = await a11yFixture()
    })

    it('aria attributes', () => {
      assert.equal(collapse.getAttribute('role'), 'group')
      assert.equal(collapse.getAttribute('aria-hidden'), 'true')
    })

    it('set opened to true', async () => {
      collapse.open = true
      await collapse.updateComplete
      assert.equal(collapse.getAttribute('aria-hidden'), 'false')
    })

    it('focus the collapse when opened', async () => {
      assert.notEqual(document.activeElement, collapse)
      collapse.open = true
      await collapse.updateComplete
      assert.equal(document.activeElement, collapse)
    })

    it('passes automated tests when closed', async () => {
      await assert.isAccessible(collapse)
    })
  })
})
