type ButtonSize = 'default' | 'mini'
type ButtonType = 'default' | 'primary' | 'warn'
type FormType = 'submit' | 'reset'
// type OpenType = 'agreePrivacyAuthorization'

export class UniButtonElement extends UniViewElementImpl implements UniCustomElement {
  static get observedAttributes() : Array<string> {
    return ['disabled', 'size', 'type', 'plain']
  }

  private borderView : UniElement
  private buttonText : UniTextElementImpl

  private disableView : UniElement | null = null

  constructor() {
    super()

    const buttonViewStyle = this.style
    buttonViewStyle.setProperty('position', 'relative')
    buttonViewStyle.setProperty('border-radius', '5px')
    buttonViewStyle.setProperty('padding-left', '14px')
    buttonViewStyle.setProperty('padding-right', '14px')

    const borderView = this.uniPage.createElement('view')
    const borderViewStyle = borderView.style
    borderViewStyle.setProperty('position', 'absolute')
    borderViewStyle.setProperty('border-style', 'solid')
    borderViewStyle.setProperty('border-width', '0.5px')
    borderViewStyle.setProperty('border-color', 'rgba(0,0,0,0.2)')
    borderViewStyle.setProperty('border-radius', '5px')
    borderViewStyle.setProperty('left', '0')
    borderViewStyle.setProperty('top', '0')
    borderViewStyle.setProperty('width', '100%')
    borderViewStyle.setProperty('height', '100%')
    // borderViewStyle.setProperty('left', '-50%')
    // borderViewStyle.setProperty('top', '-50%')
    // borderViewStyle.setProperty('width', '200%')
    // borderViewStyle.setProperty('height', '200%')
    // borderViewStyle.setProperty('transform', 'scale(0.5)')

    const buttonText = this.uniPage.createElement('text') as UniTextElementImpl
    buttonText.style.setProperty('fontSize', '18px')
    buttonText.style.setProperty('textAlign', 'center')

    this.borderView = borderView
    this.buttonText = buttonText

    this.addEventListener('click', (e: UniPointerEvent) => {
      "[weak self]"
      if (this.disabled) {
        // TODO 暂不支持同级监听拦截
        e.stopPropagation()
        return
      }
      this.checkFormAndSubmitOrReset()
      this.checkOpenType()
    })

    this.setHoverClass('')
  }

  connectedCallback() {
    super.appendChild(this.borderView)
    super.appendChild(this.buttonText)
    this.setSizeStyle(this.size as ButtonSize)
    this.setTypeStyle(this.type as ButtonType)
    // TODO
    this.style.setProperty('color', this.getForegroundColor(this.type as ButtonType))

    if (this.disabled) {
      this.appendDisabledView()
    }
  }

  disconnectedCallback() {
    this.buttonText.remove()
    this.borderView.remove()
    this.removeDisabledView()
  }

  attributeChangedCallback(name : string, oldValue : any | null, newValue : any | null) {
    switch (name) {
      case 'disabled':
        this.setTypeStyle(this.type as ButtonType)
        if (this.isConnected) {
          const disabledValue = (typeof newValue == 'string' || newValue == true) ? true : false
          if (disabledValue) {
            this.appendDisabledView()
          } else {
            this.removeDisabledView()
          }
        }
        break;
      case 'size':
        this.setSizeStyle(newValue as ButtonSize)
        break;
      case 'type':
        this.setTypeStyle(newValue as ButtonType)
        break;
      case 'plain':
        if (this.plain) {
          this.style.setProperty('border-style', 'solid')
          this.style.setProperty('border-width', '1px')
          this.borderView.style.setProperty('display', 'none')
        }
        this.setTypeStyle(this.type as ButtonType)
        break;
      case 'value':
        this.updateInnerText(this)
        break;
    }
  }

  override appendChild(child : UniElement) : void {
    this.updateInnerText(child)
  }

  override setAnyAttribute(key : string, value : Object) : void {
    if (key == 'hoverClass' && !(value instanceof Map)) {
      this.setHoverClass(value as string)
      return
    }
    super.setAnyAttribute(key, value)
  }

  // TODO
  override parseStyle(key : string, value : Object) : boolean {
    if (key == 'color') {
      if (this.plain) {
        this.buttonText.style.setProperty('color', this.getForegroundColor(this.type as ButtonType))
      }
      return this.buttonText.parseStyle(key, value)
    }
    return super.parseStyle(key, value)
  }

  override updateStyle(style : Map<string, string>) : void {
    super.updateStyle(style)

    style.forEach((value, key) => {
      switch (key) {
        case 'backgroundColor':
          break;
        case 'borderTopLeftRadius':
        case 'borderTopRightRadius':
        case 'borderBottomRightRadius':
        case 'borderBottomLeftRadius':
        case 'borderLeftWidth':
        case 'borderTopWidth':
        case 'borderRightWidth':
        case 'borderBottomWidth':
        case 'borderTopStyle':
        case 'borderRightStyle':
        case 'borderLeftStyle':
        case 'borderBottomStyle':
        case 'borderLeftColor':
        case 'borderTopColor':
        case 'borderRightColor':
        case 'borderBottomColor':
          this.style.setProperty(key, value)
          this.borderView.style.setProperty('display', 'none')
          break;
        case 'color':
        case 'textAlign':
        case 'fontSize':
        case 'fontWeight':
        case 'lineHeight':
          this.buttonText.style.setProperty(key, value)
          break;
        default:
          break;
      }
    })
  }

  // TODO 因不支持同级监听拦截, 暂时通过子覆盖方式拦截
  private appendDisabledView() {
    if (this.disableView == null) {
      this.disableView = this.uniPage.createElement('view')
      const disableViewStyle = this.disableView!.style
      disableViewStyle.setProperty('position', 'absolute')
      disableViewStyle.setProperty('left', '0')
      disableViewStyle.setProperty('top', '0')
      disableViewStyle.setProperty('width', '100%')
      disableViewStyle.setProperty('height', '100%')
      this.disableView!.addEventListener('click', (e: UniPointerEvent) => {
        "[weak self]"
        e.stopPropagation()
      })
    }
    super.appendChild(this.disableView!)
  }

  private removeDisabledView() {
    if (this.disableView != null) {
      this.disableView.remove()
    }
  }

  get disabled() : boolean {
    return this.getAttribute('disabled') == 'true' ? true : false
  }

  get size() : string {
    return this.getAttributeValue('size', 'default')
  }

  get type() : string {
    return this.getAttributeValue('type', 'default')
  }

  get plain() : boolean {
    return this.getAttribute('plain') == 'true' ? true : false
  }

  private getAttributeValue(key : string, defaultValue : string) : string {
    const value = this.getAttribute(key)
    if (value && value.length) {
      return value
    }
    return defaultValue
  }

  private updateInnerText(child : UniElement) {
    this.buttonText.setAttribute('value', child.getAttribute('value') ?? "")
  }

  private setSizeStyle(size : ButtonSize) {
    switch (size) {
      case 'default':
        this.buttonText.style.setProperty('fontSize', '18px')
        this.buttonText.style.setProperty('lineHeight', '2.55555556')
        break;
      case 'mini':
        this.buttonText.style.setProperty('fontSize', '13px')
        this.buttonText.style.setProperty('lineHeight', '2.3')
        break;
    }
  }

  private setTypeStyle(type : ButtonType) {
    // background-color
    this.style.setProperty('background-color', this.getBackgroundColor(type))

    // text-color
    this.buttonText.style.setProperty('color', this.getForegroundColor(type))

    if (this.plain) {
      this.style.setProperty('border-color', this.getBorderColor(type))
    }
  }

  private setHoverClass(value : string) {
    if (this.disabled) {
      return
    }

    const buttonType = this.type as ButtonType

    const style = new Map<string, string>()
    if (typeof value === 'string' && value == 'none') {
      style.set('backgroundColor', this.getBackgroundColor(buttonType))
    } else {
      style.set('backgroundColor', this.getBackgroundHoverColor(buttonType))
    }

    if (this.plain) {
      // TODO
      style.set('color', this.getForegroundHoverColor(buttonType))
      style.set('borderColor', this.getBorderHoverColor(buttonType))

      this.buttonText.style.setProperty('color', this.getForegroundColor(buttonType))
    }

    this.setAnyAttribute('hoverClass', style)
  }

  private getBackgroundColor(type : ButtonType) : string {
    // disabled
    if (this.disabled) {
      // plain
      if (this.plain) {
        return 'rgba(0,0,0,0.2)'
      }

      if (type == 'primary') {
        return 'rgba(0,122,255,0.6)'
      } else if (type == 'warn') {
        return '#ec8b89'
      } else {
        return '#f7f7f7'
      }
    }

    // plain
    if (this.plain) {
      return 'transparent'
    }

    // normal
    if (type == 'primary') {
      return '#007aff'
    } else if (type == 'warn') {
      return '#e64340'
    } else {
      return '#f7f7f7'
    }
  }

  private getBackgroundHoverColor(type : ButtonType) : string {
    // plain
    if (this.plain) {
      return 'transparent'
    }

    // normal
    if (type == 'primary') {
      return '#0062cc'
    } else if (type == 'warn') {
      return '#ce3c39'
    } else {
      return '#dedede'
    }
  }

  private getBorderColor(type : ButtonType) : string {
    // disabled
    if (this.disabled) {
      if (type == 'primary') {
        return 'rgba(0,0,0,0.2)'
      } else if (type == 'warn') {
        return 'rgba(0,0,0,0.2)'
      } else {
        return 'rgba(0,0,0,0.3)'
      }
    }

    // plain
    if (this.plain) {
      if (type == 'primary') {
        return '#007aff'
      } else if (type == 'warn') {
        return '#e64340'
      } else {
        return '#353535'
      }
    }

    // normal
    if (type == 'primary') {
      return 'rgba(0,0,0,0.2)'
    } else if (type == 'warn') {
      return 'rgba(0,0,0,0.2)'
    } else {
      return 'rgba(0,0,0,0.2)'
    }
  }

  private getBorderHoverColor(type : ButtonType) : string {
    // disabled
    if (this.disabled) {
      if (type == 'primary') {
        return 'rgba(0,0,0,0.2)'
      } else if (type == 'warn') {
        return 'rgba(0,0,0,0.2)'
      } else {
        return 'rgba(0,0,0,0.3)'
      }
    }

    // plain
    if (this.plain) {
      if (type == 'primary') {
        return 'rgba(0, 122, 255, 0.6)'
      } else if (type == 'warn') {
        return 'rgba(230, 67, 64, 0.6)'
      } else {
        return 'rgba(53,53,53,0.6)'
      }
    }

    // normal
    if (type == 'primary') {
      return 'rgba(0,0,0,0.2)'
    } else if (type == 'warn') {
      return 'rgba(0,0,0,0.2)'
    } else {
      return 'rgba(0,0,0,0.2)'
    }
  }

  private getForegroundColor(type : ButtonType) : string {
    // disabled
    if (this.disabled) {
      return 'rgba(0,0,0,0.3)'
    }

    // plain
    if (this.plain) {
      if (type == 'primary') {
        return '#007aff'
      } else if (type == 'warn') {
        return '#e64340'
      } else {
        return '#353535'
      }
    }

    // normal
    if (type == 'primary') {
      return '#fff'
    } else if (type == 'warn') {
      return '#fff'
    } else {
      return '#000'
    }
  }

  private getForegroundHoverColor(type : ButtonType) : string {
    // disabled
    if (this.disabled) {
      return 'rgba(0,0,0,0.3)'
    }

    // plain
    if (this.plain) {
      if (type == 'primary') {
        return 'rgba(0, 122, 255, 0.6)'
      } else if (type == 'warn') {
        return 'rgba(230, 67, 64, 0.6)'
      } else {
        return 'rgba(53,53,53,0.6)'
      }
    }

    // normal
    if (type == 'primary') {
      return '#fff'
    } else if (type == 'warn') {
      return '#fff'
    } else {
      return '#000'
    }
  }

  private checkFormAndSubmitOrReset() {
    const formType = this.getAttribute('form-type') as FormType
    if (formType != 'submit' && formType != 'reset') {
      return
    }

    let formElement = this.parentElement
    let maxRecursiveDeep = 256
    while (formElement && maxRecursiveDeep > 0) {
      maxRecursiveDeep--
      if (formElement.tagName == 'FORM') {
        break
      }
      formElement = formElement.parentElement
    }

    if (formElement) {
      const uniFormControl = formElement as any as IUniForm
      if (formType == 'submit') {
        uniFormControl.submit()
      } else if (formType == 'reset') {
        uniFormControl.reset()
      }
    }
  }

  private checkOpenType() {
    const type = this.getAttribute('openType')
    switch (type) {
      case "agreePrivacyAuthorization":
        // #ifdef APP-ANDROID
        UTSAndroid.setPrivacyAgree(true)
        // #endif
        // #ifdef APP-IOS
        UTSiOS.setPrivacyAgree(true)
        // #endif
        // #ifdef APP-HARMONY
        UTSHarmony.setPrivacyAgree(true)
        // #endif
        break;
    }
  }
}