import { classMap } from 'lit/directives/class-map.js'
import { customElement, property, state } from 'lit/decorators.js'
import { html, nothing } from 'lit'
import { PktElement } from '@/base-elements/element'
import { Ref, createRef, ref } from 'lit/directives/ref.js'
import { styleMap } from 'lit/directives/style-map.js'
import { uuidish } from '@/utils/stringutils'

import '@/components/icon'
import { TAriaLive } from '@/types/aria'

export type TProgressbarRole = 'progressbar' | 'meter'
export type TProgressbarSkin = 'dark-blue' | 'light-blue' | 'green' | 'red'
export type TProgressbarStatusPlacement = 'center' | 'left' | 'following'
export type TProgressbarStatusType = 'none' | 'percentage' | 'fraction'
export type TProgressbarTitlePosition = 'left' | 'center'

export interface IPktProgressbar {
  ariaLabel?: string | null
  ariaLabelledby?: string | null
  ariaLive?: TAriaLive | null
  ariaValueText?: string | null
  id?: string | null
  role?: TProgressbarRole
  skin?: TProgressbarSkin
  statusPlacement?: TProgressbarStatusPlacement
  statusType?: TProgressbarStatusType
  title?: string | null
  titlePosition?: TProgressbarTitlePosition
  valueCurrent: number
  valueMax?: number
  valueMin?: number
}

@customElement('pkt-progressbar')
export class PktProgressbar extends PktElement implements IPktProgressbar {
  constructor() {
    super()
  }
  // Public properties
  @property({ type: String }) ariaLabel: string | null = null
  @property({ type: String, reflect: true }) ariaLabelledby: string | null = null
  @property({ type: String, reflect: true }) ariaValueText: string | null = null
  @property({ type: String }) ariaLive: TAriaLive = 'polite'
  @property({ type: String, reflect: true }) id: string = uuidish()
  @property({ type: String }) role: TProgressbarRole = 'progressbar'
  @property({ type: String }) skin: TProgressbarSkin = 'dark-blue'
  @property({ type: String }) statusPlacement: TProgressbarStatusPlacement = 'following'
  @property({ type: String }) statusType: TProgressbarStatusType = 'none'
  @property({ type: String, reflect: true }) title = ''
  @property({ type: String }) titlePosition: TProgressbarTitlePosition = 'left'
  @property({ type: Number, reflect: true }) valueCurrent = 0
  @property({ type: Number }) valueMax = 100
  @property({ type: Number }) valueMin = 0

  // State
  @state() private labelWidth = 0
  @state() private progressbarId: string = this.id
  @state() private computedAriaLabelledby: string | null = null
  @state() private computedAriaValueText = ''

  // Private refs
  private labelRef: Ref<HTMLSpanElement> = createRef()
  private progressBarRef: Ref<HTMLDivElement> = createRef()

  firstUpdated(changedProperties: Map<string | number | symbol, unknown>) {
    super.firstUpdated(changedProperties)
    this.setComputedValues()
    this.syncAttributes()
  }

  updated(changedProperties: Map<string | number | symbol, unknown>) {
    super.updated(changedProperties)

    if (changedProperties.has('valueCurrent') && this.labelRef.value) {
      this.labelWidth = this.labelRef.value.getBoundingClientRect().width || 0
    }
    if (changedProperties.has('id') && this.id) {
      this.progressBarId = this.id
    }

    if (
      changedProperties.has('statusType') ||
      changedProperties.has('id') ||
      changedProperties.has('ariaLabelledby')
    ) {
      this.progressbarId = this.id || uuidish()
      this.computedAriaLabelledby = this.ariaLabelledby || `${this.progressbarId}-title`
    }
    if (
      changedProperties.has('ariaValueText') ||
      changedProperties.has('valueCurrent') ||
      changedProperties.has('valueMax')
    ) {
      this.computedAriaValueText =
        this.statusType === 'fraction' && !this.ariaValueText
          ? `${this.valueCurrent} av ${this.valueMax - this.valueMin}`
          : this.ariaValueText || ''
    }

    this.syncAttributes()
  }

  render() {
    const hasStatus = this.statusType !== 'none'
    const totalSteps = this.valueMax - this.valueMin
    const percentageOfSteps = (this.valueCurrent / totalSteps) * 100
    const currentPercentage =
      this.statusType === 'fraction'
        ? Math.round(percentageOfSteps)
        : Math.round(((this.valueCurrent - this.valueMin) / (this.valueMax - this.valueMin)) * 100)

    const formattedTitle = `${this.valueCurrent} av ${totalSteps}`

    const barClasses = classMap({
      'pkt-progressbar__bar': true,
      [`pkt-progressbar__bar--${this.skin}`]: !!this.skin,
    })
    const titleClasses = classMap({
      'pkt-progressbar__title': true,
      [`pkt-progressbar__title-center`]: this.titlePosition === 'center',
    })
    const statusClasses = classMap({
      'pkt-progressbar__status': true,
      [`pkt-progressbar__status--center`]: this.statusPlacement === 'center',
    })
    const placementClasses = classMap({
      [`pkt-progressbar__status-placement--following`]: this.statusPlacement === 'following',
      [`pkt-progressbar__status-placement--center`]: this.statusPlacement === 'center',
      [`pkt-progressbar__status-placement--left`]: this.statusPlacement === 'left',
    })

    return html` <div
      class="pkt-progressbar__container"
      .ref=${this.progressBarRef}
      style=${styleMap({
        '--pkt-progress-label-width': `${this.labelWidth}px`,
        '--pkt-progress-width': `${currentPercentage}%`,
      })}
    >
      ${this.title
        ? html`<p id=${`${this.progressBarId}-title`} class=${titleClasses}>${this.title}</p>`
        : nothing}

      <div class="pkt-progressbar__bar-wrapper">
        <div class=${barClasses}></div>
      </div>

      ${hasStatus
        ? html`<div class=${statusClasses}>
            <span class=${placementClasses} ${ref(this.labelRef)}>
              ${this.statusType === 'percentage' ? `${currentPercentage}%` : formattedTitle}
            </span>
          </div>`
        : nothing}
    </div>`
  }

  // methods
  private setComputedValues() {
    this.progressbarId = this.id || uuidish()
    this.computedAriaLabelledby = this.ariaLabelledby || `${this.progressbarId}-title`
    this.computedAriaValueText =
      this.statusType === 'fraction' && !this.ariaValueText
        ? `${this.valueCurrent} av ${this.valueMax - this.valueMin}`
        : this.ariaValueText || ''
  }

  private syncAttributes() {
    this._handleAttribute('aria-live', this.ariaLive)
    this._handleAttribute('aria-valuenow', this.valueCurrent)
    this._handleAttribute('aria-valuemin', this.valueMin)
    this._handleAttribute('aria-valuemax', this.valueMax)
    this._handleAttribute('aria-valuetext', this.computedAriaValueText)
    this._handleAttribute('aria-label', this.ariaLabel)
    this._handleAttribute('role', this.role)
    this._handleAttribute('aria-atomic', 'true')
    this._handleAttribute('id', this.progressbarId)
    if (!this.ariaLabel) {
      this._handleAttribute('aria-labelledby', this.computedAriaLabelledby)
    }
  }

  private _handleAttribute(name: string, value: string | number | null) {
    if (value == null || value === '') {
      this.removeAttribute(name)
    } else {
      this.setAttribute(name, String(value))
    }
  }
}

export default PktProgressbar
