import { PktElement } from '@/base-elements/element'
import { PktSlotController } from '@/controllers/pkt-slot-controller'
import { ref, Ref, createRef } from 'lit/directives/ref.js'
import { ifDefined } from 'lit/directives/if-defined.js'
import { html, nothing, PropertyValues } from 'lit'
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
import { classMap } from 'lit/directives/class-map.js'
import { customElement, property } from 'lit/decorators.js'
import { ElementProps } from '@/types/typeUtils'
import { uuidish } from '@/utils/stringutils'
import specs from 'componentSpecs/input-wrapper.json'
import '@/components/helptext'
import '@/components/icon'

type TCounterPosition = 'top' | 'bottom'
type Props = ElementProps<
  PktInputWrapper,
  | 'forId'
  | 'label'
  | 'helptext'
  | 'helptextDropdown'
  | 'helptextDropdownButton'
  | 'counter'
  | 'counterCurrent'
  | 'counterMaxLength'
  | 'optionalTag'
  | 'optionalText'
  | 'requiredTag'
  | 'requiredText'
  | 'tagText'
  | 'hasError'
  | 'errorMessage'
  | 'disabled'
  | 'inline'
  | 'ariaDescribedby'
  | 'hasFieldset'
  | 'useWrapper'
  | 'role'
>

// TODO: Trekk denne logikken ut i utils, så den kan gjenbrukes andre steder
type Booleanish = boolean | 'true' | 'false'
const booleanishConverter = {
  fromAttribute(value: string | boolean | null): boolean {
    // Accept false, "false", undefined, null as false
    if (value === null || value === undefined) return false
    if (value === '' || value === 'true' || value === true) return true
    if (value === 'false' || value === false) return false
    return Boolean(value)
  },
  toAttribute(value: boolean): string | null {
    // Always reflect as "true" or "false"
    return value ? 'true' : 'false'
  },
}

@customElement('pkt-input-wrapper')
export class PktInputWrapper extends PktElement<Props> {
  defaultSlot: Ref<HTMLElement> = createRef()
  helptextSlot: Ref<HTMLElement> = createRef()

  constructor() {
    super()
    this.slotController = new PktSlotController(this, this.defaultSlot, this.helptextSlot)
  }

  /**
   * Element attributes
   */
  @property({ type: String }) forId: string = uuidish()
  @property({ type: String }) label: string = ''
  @property({ type: String }) helptext: string | null = null
  @property({ type: String }) helptextDropdown: string | null = null
  @property({ type: String }) helptextDropdownButton: string | null = null
  @property({ type: Boolean }) counter: boolean = specs.props.counter.default
  @property({ type: Number }) counterCurrent: number = 0
  @property({ type: Number }) counterMaxLength: number = 0

  @property({ type: String }) counterError: string | null = null
  @property({ type: String, reflect: false }) counterPosition: TCounterPosition = 'bottom'
  @property({ type: Boolean }) optionalTag: boolean = specs.props.optionalTag.default
  @property({ type: String }) optionalText: string = specs.props.optionalText.default
  @property({ type: Boolean }) requiredTag: boolean = specs.props.requiredTag.default
  @property({ type: String }) requiredText: string = specs.props.requiredText.default
  @property({ type: String }) tagText: string | null = null
  @property({ type: Boolean }) hasError: boolean = specs.props.hasError.default
  @property({ type: String }) errorMessage: string = ''
  @property({ type: Boolean }) disabled: boolean = specs.props.disabled.default
  @property({ type: Boolean }) inline: boolean = specs.props.inline.default
  @property({ type: String }) ariaDescribedby: string | undefined = undefined
  @property({ type: Boolean }) hasFieldset: boolean = specs.props.hasFieldset.default
  @property({ type: String, reflect: true }) role: string | null = 'group'
  @property({ type: Boolean, reflect: true, converter: booleanishConverter })
  useWrapper: Booleanish = specs.props.useWrapper.default

  protected updated(changedProperties: PropertyValues): void {
    super.updated(changedProperties)
  }

  render() {
    const classes = {
      'pkt-inputwrapper': true,
      'pkt-inputwrapper--error': this.hasError,
      'pkt-inputwrapper--disabled': this.disabled,
      'pkt-inputwrapper--inline': this.inline,
    }

    const tagClasses = 'pkt-tag pkt-tag--small pkt-tag--thin-text'

    const describedBy = this.ariaDescribedby
      ? this.ariaDescribedby
      : this.helptext
        ? `${this.forId}-helptext`
        : nothing

    const tagElement = () => {
      return html`
        ${this.tagText
          ? html`<span class=${tagClasses + ' pkt-tag--gray'}>${this.tagText}</span>`
          : nothing}
        ${this.optionalTag
          ? html`<span class=${tagClasses + ' pkt-tag--blue-light'}>${this.optionalText}</span>`
          : nothing}
        ${this.requiredTag
          ? html`<span class=${tagClasses + ' pkt-tag--beige'}>${this.requiredText}</span>`
          : nothing}
      `
    }

    const labelElement = () => {
      if (this.useWrapper) {
        if (this.hasFieldset) {
          return html`<legend
            class="pkt-inputwrapper__legend"
            id="${this.forId}-label"
            @click=${this.handleLabelClick}
          >
            ${this.label} ${tagElement()}
          </legend>`
        } else {
          return html`<label
            class="pkt-inputwrapper__label"
            for="${this.forId}"
            aria-describedby="${describedBy}"
            id="${this.forId}-label"
            @click=${this.handleLabelClick}
            >${this.label}${tagElement()}</label
          >`
        }
      } else {
        return html`<label
          for="${this.forId}"
          class="pkt-sr-only"
          aria-describedby="${describedBy}"
          id="${this.forId}-label"
        >
          ${this.label}
        </label>`
      }
    }

    const helptextElement = () => {
      return html`
        <pkt-helptext
          class="${ifDefined(!this.useWrapper ? 'pkt-hide' : undefined)}"
          .forId=${this.forId}
          .helptext=${this.helptext}
          .helptextDropdown=${this.helptextDropdown}
          .helptextDropdownButton=${this.helptextDropdownButton ||
          specs.props.helptextDropdownButton.default}
          @toggleHelpText=${(e: CustomEvent) => {
            this.toggleDropdown(e)
          }}
          ${ref(this.helptextSlot)}
          name="helptext"
        ></pkt-helptext>
      `
    }

    const counterElement = () => {
      if (this.counter) {
        return html`<div class="pkt-input__counter" aria-live="polite" aria-atomic="true">
          ${this.counterError ? this.counterError : nothing} ${this.counterCurrent || 0}
          ${this.counterMaxLength ? `/${this.counterMaxLength}` : nothing}
        </div>`
      } else {
        return nothing
      }
    }

    const errorElement = () => {
      if (this.hasError && this.errorMessage) {
        return html`<div
          role="alert"
          class="pkt-alert pkt-alert--error pkt-alert--compact"
          aria-live="assertive"
          aria-atomic="true"
          id="${this.forId}-error"
        >
          <pkt-icon name="alert-error" class="pkt-alert__icon"></pkt-icon>
          <div class="pkt-alert__text">${unsafeHTML(this.errorMessage)}</div>
        </div>`
      } else {
        return nothing
      }
    }

    const inputContent = () => {
      //prettier-ignore
      return html`
        ${labelElement()} 
        ${helptextElement()}
        ${this.counterPosition === 'top' ? counterElement() : nothing}
        <div class="pkt-contents" ${ref(this.defaultSlot)}></div>
        ${this.counterPosition === 'bottom' ? counterElement() : nothing} 
        ${errorElement()}
      `
    }

    const wrapperElement = () => {
      return this.hasFieldset
        ? html`<fieldset class="pkt-inputwrapper__fieldset" aria-describedby="${describedBy}">
            ${inputContent()}
          </fieldset>`
        : html`<div class="pkt-inputwrapper__fieldset">${inputContent()}</div>`
    }

    return html`<div class=${classMap(classes)}>${wrapperElement()}</div> `
  }

  private toggleDropdown(e: CustomEvent) {
    this.dispatchEvent(
      new CustomEvent('toggleHelpText', {
        bubbles: false,
        detail: { isOpen: e.detail.isOpen },
      }),
    )
  }

  private handleLabelClick(e: MouseEvent) {
    if (this.disabled) {
      e.preventDefault()
      e.stopImmediatePropagation()
    }
    this.dispatchEvent(
      new CustomEvent('labelClick', {
        bubbles: true,
        composed: true,
        detail: 'label clicked',
      }),
    )
  }
}
