import { html, nothing, TemplateResult } from 'lit'
import { property, queryAsync, state } from 'lit/decorators.js'
import { ClassInfo, classMap } from 'lit/directives/class-map.js'
import { when } from 'lit/directives/when.js'
import { EndPressConfig } from '../../../controllers/ActionController.js'
import UiRipple from '../../ripple/internals/ripple.js'
import { ripple } from '../../effects/rippleDirective.js'
import { isDisabled, setDisabled } from '../../../lib/disabled.js'
import { UiElement } from '../../UiElement.js'
import type { TabsPriority } from './Tabs.js'
import { Easing } from '../../motion/animation.js'
import '../../ripple/ui-ripple.js'

export default class UiTab extends UiElement {
  get disabled(): boolean {
    return isDisabled(this)
  }

  /**
   * When set, the button is a disabled state.
   * @attribute
   */
  @property({ reflect: true, type: Boolean })
  set disabled(value: boolean) {
    const old = isDisabled(this)
    setDisabled(this, value)
    this.requestUpdate('disabled', old)
  }

  /**
   * Whether the tab is selected. Note, this does not correspond to the `selected` state
   * in the `ui-tabs`. This is only to render the tab in the selected state.
   * @attribute
   */
  @property({ reflect: true, type: Boolean }) accessor selected = false

  /**
   * @attribute
   */
  @property({ reflect: true, type: Boolean }) accessor priority: TabsPriority = 'primary'

  /**
   * @attribute
   */
  @property({ reflect: true, type: Boolean }) accessor indicated = false

  @state() protected accessor hasIcon = false

  @state() protected accessor showRipple = false

  @queryAsync('ui-ripple') protected accessor ripple!: Promise<UiRipple | null>

  protected readonly getRipple = (): Promise<UiRipple | null> => {
    this.showRipple = true
    return this.ripple
  }

  constructor() {
    super()
    this.actionController.cancelKeyboardEvents = true
    this.addEventListener('keydown', this.handleKeyDown.bind(this))
    this.addEventListener('keyup', this.handleKeyUp.bind(this))
    this.addEventListener('click', this.handleClick.bind(this))
    this.addEventListener('pointerdown', this.handlePointerDown.bind(this))
    this.addEventListener('pointerup', this.handlePointerUp.bind(this))
    this.addEventListener('pointercancel', this.handlePointerCancel.bind(this))
    this.addEventListener('pointerleave', this.handlePointerLeave.bind(this))
    this.addEventListener('contextmenu', this.handleContextMenu.bind(this))
  }

  override connectedCallback(): void {
    super.connectedCallback()
    if (!this.hasAttribute('role')) {
      this.setAttribute('role', 'tab')
    }
    if (!this.hasAttribute('aria-selected')) {
      this.setAttribute('aria-selected', 'false')
    }
  }

  override endPress(config: EndPressConfig): void {
    super.endPress(config)
    const { cancelled, reason } = config
    if (cancelled) {
      return
    }
    if (reason === 'enter' || reason === 'space') {
      this.click()
    }
  }

  /**
   * Sets the `_hasIcon` state property when the "icon" slot change event is dispatched.
   */
  protected handleIconChange(e: Event): void {
    const slot = e.target as HTMLSlotElement
    this.hasIcon = !!slot.assignedNodes().length
  }

  /**
   * When `indicated` is `true` it animates the indicator to highlight the position of the tab.
   */
  highlight(): void {
    if (!this.indicated) {
      return
    }
    const pointer = this.shadowRoot?.querySelector('.pointer')
    if (!pointer) {
      return
    }
    const frames: Keyframe[] = [
      {
        width: `40px`,
      },
      {
        width: `80px`,
      },
      {
        width: `40px`,
      },
    ]

    pointer.animate(frames, {
      duration: 360,
      iterations: 1,
      easing: Easing.STANDARD,
    })
  }

  protected override render(): TemplateResult {
    const { pressed = false } = this
    const containerClasses = classMap({
      surface: true,
      withIcon: this.hasIcon,
      pressed,
    })
    return html`
      <div class="${containerClasses}" ${ripple(this.getRipple)}>
        <div class="container"></div>
        ${when(this.showRipple, this.renderRipple)}
        <div class="content">
          ${this.renderIcon()}
          <div class="tab-content"><slot></slot></div>
        </div>
        ${this.renderIndicator()}
      </div>
    `
  }

  protected renderIcon(): TemplateResult {
    return html`
      <div class="icon">
        <slot name="icon" @slotchange="${this.handleIconChange}"></slot>
      </div>
    `
  }

  protected renderRipple = (): TemplateResult => {
    const { disabled } = this
    return html`<ui-ripple class="ripple" ?disabled="${disabled}"></ui-ripple>`
  }

  protected renderIndicator(): TemplateResult | typeof nothing {
    const { indicated, priority } = this
    if (!indicated) {
      return nothing
    }
    const classes: ClassInfo = {
      indicator: true,
      primary: priority === 'primary',
    }
    return html`<div class="${classMap(classes)}">
      <span role="presentation" class="pointer"></span>
    </div>`
  }
}
