import { html, TemplateResult, PropertyValues, SVGTemplateResult, svg } from 'lit'
import { property, query, state } from 'lit/decorators.js'
import { consume } from '@lit/context'
import { UiElement } from '../../../md/UiElement.js'
import { setDisabled } from '../../../lib/disabled.js'
import type UiRipple from '../../../md/ripple/internals/ripple.js'
import type { BeginPressConfig, EndPressConfig } from '../../../controllers/ActionController.js'
import { type IUser, userContext } from '../../../providers/user.js'
import '../../../md/focus-ring/ui-focus-ring.js'
import '../../../md/ripple/ui-ripple.js'

export type AvatarType = 'button' | 'icon'

export default class UserAvatar extends UiElement {
  @query('ui-ripple') accessor ripple: UiRipple | null = null
  /**
   * Whether the avatar is disabled.
   * @attribute
   */
  @property({ type: Boolean, reflect: true }) accessor disabled = false
  /**
   * Set with the user. The computed user initials.
   * @attribute
   */
  @state() protected accessor userInitials: string | undefined

  /**
   * The URL to the user picture.
   * @attribute
   */
  @state() protected accessor userPicture: string | undefined

  /**
   * The user object to display the avatar for.
   *
   * This property consumes the `userContext`.
   */
  @property({ type: Object, attribute: false })
  @consume({ context: userContext })
  accessor user: IUser | undefined
  /**
   * The type of avatar to render, either 'button' or 'icon'.
   * @attribute
   */
  @property({ type: String }) accessor type: AvatarType = 'button'

  constructor() {
    super()

    this.addEventListener('keydown', this.handleKeyDown.bind(this))
    this.addEventListener('keyup', this.handleKeyUp.bind(this))
    this.addEventListener('pointerenter', this.handlePointerEnter.bind(this))
    this.addEventListener('pointerleave', this.handlePointerLeave.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('contextmenu', this.handleContextMenu.bind(this))
  }

  protected override willUpdate(cp: PropertyValues<this>): void {
    if (cp.has('user')) {
      this.handleUserChange(this.user)
    }
    if (cp.has('disabled')) {
      setDisabled(this, cp.get('disabled'))
    }
    super.willUpdate(cp)
  }

  override beginPress(options: BeginPressConfig): void {
    if (!this.shouldHandleInteraction()) {
      return
    }
    super.beginPress(options)
    this.ripple?.beginPress(options.positionEvent)
  }

  override endPress(options: EndPressConfig): void {
    if (!this.shouldHandleInteraction()) {
      return
    }
    super.endPress(options)
    this.ripple?.endPress()
  }

  override handleKeyDown(e: KeyboardEvent): void {
    if (!this.shouldHandleInteraction()) {
      e.stopPropagation()
      e.preventDefault()
      return
    }
    if (e.code === 'Tab' || e.shiftKey) return
    // do not prevent default, so that parent elements can handle the key event
    this.beginPress({ positionEvent: e })
  }

  override handleKeyUp(e: KeyboardEvent): void {
    if (!this.shouldHandleInteraction()) {
      e.stopPropagation()
      e.preventDefault()
      return
    }

    // do not prevent default, so that parent elements can handle the key event
    this.endPress({ cancelled: false, actionData: { item: this } })
  }

  override handlePointerEnter(e: PointerEvent): void {
    if (!this.shouldHandleInteraction()) {
      e.stopPropagation()
      e.preventDefault()
      return
    }
    this.ripple?.beginHover(e)
  }

  override handlePointerLeave(e: PointerEvent): void {
    if (!this.shouldHandleInteraction()) {
      e.stopPropagation()
      e.preventDefault()
      return
    }
    super.handlePointerLeave(e)

    this.ripple?.endHover()
  }

  override handleClick(e: MouseEvent): void {
    super.handleClick(e)
    if (this.disabled) {
      e.preventDefault()
      e.stopPropagation()
      return
    }
    this.endPress({ cancelled: false, actionData: { event: e } })
  }

  protected shouldHandleInteraction(): boolean {
    return !this.disabled && this.type === 'button'
  }

  /**
   * Handles changes to the user property.
   * @param user The user object
   */
  protected handleUserChange(user?: IUser): void {
    if (user) {
      this.userInitials = this.readUserInitials(user)
      this.userPicture = user.picture && user.picture.url
    } else {
      this.userInitials = undefined
      this.userPicture = undefined
    }
  }

  protected readUserInitials(user: IUser): string | undefined {
    const { name } = user
    if (!name) {
      return undefined
    }
    const max = 2
    const parts = name
      .split(/[\s-]/)
      .slice(0, max)
      .filter((i) => !!i)
      .map((i) => i[0])
    return parts.join('')
  }

  protected handlePictureError(): void {
    this.userPicture = undefined
  }

  /**
   * @return Template result for an icon
   */
  override render(): TemplateResult {
    const { userPicture } = this
    let content: TemplateResult
    if (userPicture) {
      content = this.pictureTemplate(userPicture)
    } else if (this.userInitials) {
      content = this.nameTemplate(this.userInitials)
    } else {
      content = this.renderDefaultAvatar()
    }
    if (this.type === 'icon') {
      return this.renderIcon(content)
    }
    return this.renderButton(content)
  }

  protected renderButton(content: TemplateResult): TemplateResult {
    return html`
      <button id="button" class="icon-button">
        ${this.renderFocusRing()} ${this.renderRipple()}
        <span role="presentation" class="icon">${content}</span>
      </button>
    `
  }

  protected renderIcon(content: TemplateResult): TemplateResult {
    return html`<span role="presentation" class="icon">${content}</span>`
  }

  protected pictureTemplate(url: string): TemplateResult {
    return html`
      <img
        src="${url}"
        alt="${this.userInitials || 'Thumb'}"
        class="user-picture user-icon"
        @error="${this.handlePictureError}"
      />
    `
  }

  /**
   * Renders a bubble with user initials
   * @param initials The user initials
   */
  protected nameTemplate(initials: string): TemplateResult {
    return html` <span class="avatar-initials user-icon">${initials}</span> `
  }

  protected renderDefaultAvatar(): TemplateResult {
    return html`<span class="avatar-default icon"> ${this.defaultIcon()} </span>`
  }

  protected defaultIcon(): SVGTemplateResult {
    return svg`
    <svg viewBox="0 0 120 120" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon">
      <rect width="120" height="120" rx="60" class="avatar-background" />
      <path
        fill-rule="evenodd"
        clip-rule="evenodd"
        d="M78.0007 48C78.0007 57.9411 69.9419 66 60.0007 66C50.0596 66 42.0007 57.9411 42.0007 48C42.0007 38.0589 50.0596 30 60.0007 30C69.9419 30 78.0007 38.0589 78.0007 48ZM72.0007 48C72.0007 54.6274 66.6282 60 60.0007 60C53.3733 60 48.0007 54.6274 48.0007 48C48.0007 41.3726 53.3733 36 60.0007 36C66.6282 36 72.0007 41.3726 72.0007 48Z"
        class="avatar-lines"
      />
      <path
        d="M60.0007 75C40.5776 75 24.0286 86.4852 17.7246 102.576C19.2603 104.101 20.878 105.543 22.5706 106.896C27.2648 92.1231 41.9909 81 60.0007 81C78.0106 81 92.7367 92.1231 97.4309 106.896C99.1235 105.544 100.741 104.101 102.277 102.576C95.973 86.4853 79.4239 75 60.0007 75Z"
        class="avatar-lines"
      />
    </svg>
    `
  }

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

  protected renderFocusRing(): TemplateResult {
    return html`<ui-focus-ring part="focus-ring" for="button"></ui-focus-ring>`
  }
}
