import { html, fixture, assert, oneEvent } from '@open-wc/testing'
import { INtlmAuthorization, IRequestAuthorization, RequestAuthorization } from '@api-client/core/browser.js'
import { Authorization } from '../../../src/elements/authorization/ui/Authorization.js'
import Input from '../../../src/md/input/Input.js'
import '../../../src/define/authorization/ntlm-authorization.js'

describe('NTLM method', () => {
  const usernameSelector = 'ui-filled-text-field[name="username"]'
  const passwordSelector = 'ui-filled-text-field[name="password"]'
  const domainSelector = 'ui-filled-text-field[name="domain"]'

  const username = 'test-username'
  const password = 'test-password'
  const domain = 'test-domain'

  async function basicFixture(auth?: IRequestAuthorization): Promise<Authorization> {
    return fixture(html`<ntlm-authorization .authorization="${auth}"></ntlm-authorization>`)
  }

  describe('DOM rendering', () => {
    let element: Authorization
    let form: HTMLFormElement

    beforeEach(async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {}).toJSON()
      element = await basicFixture(auth)
      form = element.shadowRoot!.querySelector('form')!
    })

    it('has form in the DOM', async () => {
      assert.ok(form)
    })

    it('has form has autocomplete', async () => {
      assert.equal(form.getAttribute('autocomplete'), 'on')
    })

    it('form has username input', async () => {
      const input = form.querySelector(usernameSelector)
      assert.ok(input)
    })

    it('username is required', async () => {
      const input = form.querySelector(usernameSelector) as Input
      assert.isTrue(input.required)
    })

    it('username has the invalid label', async () => {
      const input = form.querySelector(usernameSelector) as Input
      assert.equal(input.invalidText, 'Username is required')
    })

    it('form has password input', async () => {
      const input = form.querySelector(passwordSelector)
      assert.ok(input)
    })

    it('password is not required', async () => {
      const input = form.querySelector(passwordSelector) as Input
      assert.notOk(input.required)
    })

    it('form has domain input', async () => {
      const input = form.querySelector(domainSelector)
      assert.ok(input)
    })

    it('domain is not required', async () => {
      const input = form.querySelector(domainSelector) as Input
      assert.notOk(input.required)
    })

    it('has no other inputs', () => {
      const controls = form.querySelectorAll('ui-filled-text-field')
      assert.lengthOf(controls, 3)
    })
  })

  describe('change notification', () => {
    let element: Authorization
    beforeEach(async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {}).toJSON()
      element = await basicFixture(auth)
    })

    it('notifies when username changes', async () => {
      const input = element.shadowRoot!.querySelector(usernameSelector) as Input
      setTimeout(() => {
        input.value = 'test'
        input.dispatchEvent(new CustomEvent('change'))
      })
      const e = await oneEvent(element, 'change')
      assert.ok(e)
    })

    it('notifies when password changes', async () => {
      const input = element.shadowRoot!.querySelector(passwordSelector) as Input
      setTimeout(() => {
        input.value = 'test'
        input.dispatchEvent(new CustomEvent('change'))
      })
      const e = await oneEvent(element, 'change')
      assert.ok(e)
    })

    it('notifies when domain changes', async () => {
      const input = element.shadowRoot!.querySelector(domainSelector) as Input
      setTimeout(() => {
        input.value = 'test'
        input.dispatchEvent(new CustomEvent('change'))
      })
      const e = await oneEvent(element, 'change')
      assert.ok(e)
    })
  })

  describe('updating the model', () => {
    let element: Authorization
    beforeEach(async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {
        username,
        password,
        domain,
      }).toJSON()
      element = await basicFixture(auth)
    })

    it('updates the username from user input', () => {
      const input = element.shadowRoot!.querySelector(usernameSelector) as Input
      input.value = 'test'
      input.dispatchEvent(new Event('change'))
      const config = element.authorization!.config as INtlmAuthorization
      assert.equal(config.username, 'test')
    })

    it('updates the password from user input', () => {
      const input = element.shadowRoot!.querySelector(passwordSelector) as Input
      input.value = 'test'
      input.dispatchEvent(new Event('change'))
      const config = element.authorization!.config as INtlmAuthorization
      assert.equal(config.password, 'test')
    })

    it('updates the domain from user input', () => {
      const input = element.shadowRoot!.querySelector(domainSelector) as Input
      input.value = 'test'
      input.dispatchEvent(new Event('change'))
      const config = element.authorization!.config as INtlmAuthorization
      assert.equal(config.domain, 'test')
    })
  })

  describe('data restoration', () => {
    let element: Authorization
    beforeEach(async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {
        username,
        password,
        domain,
      }).toJSON()
      element = await basicFixture(auth)
    })

    it('has the value on the username filed', () => {
      const input = element.shadowRoot!.querySelector(usernameSelector) as Input
      assert.equal(input.value, username)
    })

    it('has the value on the password filed', () => {
      const input = element.shadowRoot!.querySelector(passwordSelector) as Input
      assert.equal(input.value, password)
    })

    it('has the value on the domain filed', () => {
      const input = element.shadowRoot!.querySelector(domainSelector) as Input
      assert.equal(input.value, domain)
    })
  })

  describe('validation', () => {
    let element: Authorization
    beforeEach(async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {}).toJSON()
      element = await basicFixture(auth)
    })

    it('is not valid without the username', () => {
      const result = element.checkValidity()
      assert.isFalse(result)
    })

    it('is valid with the username', async () => {
      ;(element.authorization!.config as INtlmAuthorization).username = 'test-username'
      element.requestUpdate()
      await element.updateComplete
      const result = element.checkValidity()
      assert.isTrue(result)
    })
  })

  describe('a11y', () => {
    it('is accessible without the model', async () => {
      const element = await basicFixture()
      await assert.isAccessible(element)
    })

    it('is accessible when empty', async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', {}).toJSON()
      const element = await basicFixture(auth)
      await assert.isAccessible(element)
    })

    it('is accessible with values', async () => {
      const auth = RequestAuthorization.fromTypedConfig('ntlm', { username, password, domain }).toJSON()
      const element = await basicFixture(auth)
      await assert.isAccessible(element)
    })
  })
})
