import Cookies from 'js-cookie'
import { LitElement, PropertyValues } from 'lit'
import { customElement, property } from 'lit/decorators.js'
import { EventEmitterMixin } from '../EventEmitterMixin'
import { WrappingMixin } from '../WrappingMixin'

interface Form {
  element: HTMLElement
  fieldData: string
  fieldUrl: string
}

@customElement('btu-toggleable')
export default class Toggleable extends EventEmitterMixin(
  WrappingMixin(LitElement),
) {
  static get tagName() {
    return 'btu-toggleable'
  }

  private _forms: Form[]
  private _root: HTMLElement
  private _select?: HTMLSelectElement

  @property({ attribute: 'root-selector' })
  rootSelector?: string

  // Overrides #find in superclass.
  find() {
    return this.querySelector<HTMLSelectElement>(':scope > select')
  }

  set child(_c: HTMLSelectElement) {
    super._child = _c
    this._select = _c
    this.addEventListener('change', this._handleChange)
    this._handleChange()
  }

  constructor() {
    super()
    this._forms = []
    this._root = window.document.body
  }

  createRenderRoot() {
    return this
  }

  disconnectedCallback(): void {
    this.removeEventListener('change', this._handleChange)
  }

  willUpdate(changedProperties: PropertyValues<this>) {
    if (changedProperties.has('rootSelector')) {
      if (this.rootSelector && typeof this.rootSelector === 'string') {
        const root = this._select?.closest(this.rootSelector)
        if (root instanceof HTMLElement) {
          this._root = root
        } else {
          console.error(
            'Querying for the rootSelector did not return a valid HTML element.',
          )
        }
      } else if (this.rootSelector != null) {
        console.error(
          'An invalid rootSelector was provided. Expected non-empty string.',
        )
      }
    }
  }

  private _handleChange() {
    if (this._select?.matches('[disabled]')) return

    const selectedOption = this._select?.options[this._select.selectedIndex]

    const convertId = this._select?.dataset.selectedId
    const convertType = this._select?.dataset.selectedType

    let convertData = ''
    let formData = ''

    if (convertId) {
      const objectForm = document.querySelector(
        '.objectInputs[data-id="' + convertId + '"]',
      )

      if (objectForm) {
        objectForm.querySelectorAll('[name]').forEach((input, key) => {
          if (
            input instanceof HTMLInputElement ||
            input instanceof HTMLTextAreaElement ||
            input instanceof HTMLSelectElement
          ) {
            const { name, value } = input
            formData +=
              (key === 0 ? '' : '&') +
              encodeURIComponent(name) +
              '=' +
              encodeURIComponent(value)
          }
        })

        if (convertType) {
          convertData =
            '&convert=true&convertType=' +
            encodeURIComponent(convertType) +
            '&convertId=' +
            encodeURIComponent(convertId)
        }
      }
    }

    this._toggle(selectedOption?.dataset.hide, true, null, null)
    this._toggle(selectedOption?.dataset.show, false, convertData, formData)

    if (selectedOption) this._setSelected(selectedOption)
  }

  private _toggle(
    selector: string | undefined,
    disable: boolean,
    convertData: string | null,
    newObjData: string | null,
  ) {
    let divsToToggle: NodeListOf<HTMLDivElement>
    const inputs: Array<HTMLElement> = []

    if (selector) {
      selector = ':scope ' + selector.replaceAll(', ', ', :scope ')
      selector = selector.replaceAll(':scope :scope ', ':scope ')
      divsToToggle = this._root.querySelectorAll(selector)

      if (!disable) {
        const toggleableForms: HTMLElement[] = []
        divsToToggle.forEach((div) => {
          const forms = div.querySelectorAll<HTMLElement>(
            ':scope > .toggleable-form[data-form-fields-url]',
          )
          forms?.forEach((el) => toggleableForms.push(el))
        })

        toggleableForms?.forEach((element) => {
          const form: Form = {
            element,
            fieldData: element.dataset.formFieldsData || '',
            fieldUrl: element.dataset.formFieldsUrl || '',
          }

          const url = form.fieldUrl
          let data = 'data=' + encodeURIComponent(form.fieldData)

          if (convertData) data += convertData
          if (newObjData) data += '&' + newObjData

          const fetchData = async () => {
            const response = await fetch(url, {
              method: 'POST',
              credentials: 'include',
              headers: {
                'Brightspot-CSRF': Cookies.get('bsp.csrf') || '',
                'Content-Type':
                  'application/x-www-form-urlencoded;charset=UTF-8',
              },
              cache: 'no-cache',
              body: data,
            })

            form.element.innerHTML = await response.text()
            if (response.ok) {
              delete form.element.dataset.formFieldsUrl
              delete form.element.dataset.formFieldsData

              form.element.dispatchEvent(
                new CustomEvent('create', { bubbles: true }),
              )
              form.element.dispatchEvent(new Event('load'))
              form.element.dispatchEvent(new UIEvent('resize'))
            } else {
              form.element.dispatchEvent(
                new CustomEvent('error', {
                  bubbles: true,
                  detail: { message: response.text(), code: response.status },
                }),
              )
            }
          }
          fetchData()

          this._forms.push(form)
        })
      }

      divsToToggle.forEach((el) => {
        el.querySelectorAll<HTMLElement>(
          'input, textarea, select, button',
        ).forEach((input) => {
          inputs.push(input)
        })

        if (el.matches('input, textarea, select, button')) {
          inputs.push(el)
        }

        el.setAttribute('style', disable ? 'display: none;' : '')
        el
          .querySelector('.toggleable')
          ?.dispatchEvent(new CustomEvent('create', { bubbles: true }))
        el.dispatchEvent(new UIEvent('resize'))
      })

      inputs.forEach((input) => {
        input.toggleAttribute('disabled', disable)
      })
    }
  }

  private _setSelected(selectedOption: HTMLOptionElement) {
    if (!this._select) return
    this._select.dataset.selectedId = selectedOption.value
    this._select.dataset.selectedType = selectedOption.dataset.type || ''
  }
}

declare global {
  interface HTMLElementTagNameMap {
    'btu-toggleable': Toggleable
  }
}
