import React from 'react'
import {Spin, Progress, Col, Row, Empty} from 'antd'
import { Renderer, RendererProps } from '../../../factory'
import { BaseSchema, SchemaApi } from '../../../Schema'
import { Icon } from '../../../components/icons'
import { deepCalcFn } from '../../../utils/utils'
import isEqual from 'lodash/isEqual'
import { IScopedContext, ScopedContext } from '../../../Scoped'
import { tokenize } from '../../../utils/tpl-builtin'

const colorObj = {
  'Display-default': '#ccc',
  'Display-error': '#f5222f',
  'Display-warning': '#faad14',
  'Display-info': '#3574ee',
  'Display-success': '#52c41a'
}

const isEmpty = (value: any) => {
  if (typeof value == 'number') {
    return false
  }
  return !!!value
}

export interface DataDisplaySchema extends BaseSchema {
  type: 'data-display'
  api: SchemaApi
  /** 
   * 每行展示分组个数
   */
  groupRowNum: number
  /**
   *  每行展示分组宽度
   */
  groupWidth: number
  /**
  * 一个分组的每行展示字段个数
  */
  columnNum: number
  /**
  * 每行分组宽度,大于0则columnNum不生效
  */
  columnWidth: number
  /**
   * 分组字段
   */
  groupField: string
  /**
  * 分组背景色字段
  */
  groupBgColorField?: string
  /**
  * 分组描述字段
  */
  groupDescField?: string
  /**
   * 分组描述字体颜色字段
   */
  groupDescColorField?: string
  /**
   * 分组border大小字段
   */
  groupBorderSizeField?: string
  /**
   * 分组border颜色字段
   */
  groupBorderColorField?: string
  /**
   * 分组padding大小字段
   */
  groupPaddingField?: string
  /**
  * 图标字段
  */
  iconField?: string
  /**
   * 图标颜色
   */
  iconColorField?: string
  /**
  * 指标描述字段
  */
  labelDescField?: string
  /**
  * 指标内容展示方向 0:水平 1:垂直
  */
  labelDirection: 0 | 1
  /**
  * 指标名称字段
  */
  labelNameField: string
  /**
   * 指标名称颜色字段
   */
  labelNameColorField?: string
  /**
  * 指标值字段
  */
  labelValueField: string
  /**
   * 指标值表达式字段,对应的值不为空时根据items计算表达式值,labelValueField不生效
   */
  valueFormulaField?: string
  /**
  * 指标值字体颜色字段
  */
  labelFontColorField?: string
  /**
  * 指标值字体大小字段
  */
  labelFontSizeField?: string
  /**
  * 进度条value字段
  */
  progressField?: string
  /**
  * 进度条位置字段 0:左 1:右 2:上 3:下
  */
  progressPositionField?: string
  /**
   * 进度条类型字段 0:环形 1:条形
   */
  progressTypeField?: string
  /**
   * 进度条颜色字段
   */
  progressColorField?: string
  /**
   * 指标linkUrl字段
   */
  linkUrlField?: string
  /**
  * 指标linkTitle字段
  */
  linkTitleField?: string
  /**
  * 指标linkId字段
  */
  linkPageField?: string
  items?: any[]
  query?: object
}

export interface DataDisplayProps extends RendererProps, Omit<DataDisplaySchema, 'type' | 'className'> { }

interface DataDisplayState {
  data: IDisplayData[]
  loading: boolean
  mostHigh: number
  progressWidth: number
}

interface ILabel {
  labelName: string,
  labelValue: string
  labelNameColor?: string
  labelIcon?: string
  labelIconColor?: string
  labelDesc?: string
  labelFontSize?: string
  labelFontColor?: string
  linkId?: string
  linkUrl?: string
  linkTitle?: string
  linkData?: object
}

interface IDisplayData {
  groupId: string | number
  groupName: string | null
  groupDesc?: string
  groupDescColor?: string
  groupBg?: string
  groupPadding?: string
  groupBorderSize?: number
  groupBorderColor?: string
  /**
  * 进度条类型字段 0:环形 1:条形
  */
  progressType?: 0 | 1
  /**
  * 进度条位置字段 0:左 1:右 2:上 3:下
  */
  progressPosition?: 0 | 1 | 2 | 3
  progressColor?: string
  progressValue?: number
  labels: ILabel[]
}

export class DataDisplay extends React.Component<DataDisplayProps, DataDisplayState> {

  rowRef: React.RefObject<HTMLDivElement>

  static defaultProps: Partial<DataDisplaySchema> = {
    columnNum: 1,
    groupRowNum: 1,
    columnWidth: 0,
    groupWidth: 0,
    labelDirection: 1
  }

  constructor(props: DataDisplayProps) {
    super(props)
    this.state = {
      progressWidth: 45,
      mostHigh: 0,//col中最高的高度
      loading: true,
      data: [],
    }
    this.rowRef = React.createRef()
  }

  componentDidMount() {
    this.getData()
    window.addEventListener('resize', this.getColHeight)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.getColHeight)
  }

  async getData(params?: object) {
    const { env, api, query = {}, items,
      groupField, groupDescField, groupDescColorField, groupBgColorField, iconField, iconColorField, groupBorderColorField, groupBorderSizeField, groupPaddingField,
      labelNameField, labelNameColorField, labelValueField, valueFormulaField, labelDescField, labelFontSizeField, labelFontColorField,
      progressTypeField, progressPositionField, progressColorField, progressField,
      linkUrlField, linkPageField, linkTitleField
    } = this.props
    this.setState({ loading: true })

    const payload = await env.fetcher(api, { ...query, ...params }).finally(() => this.setState({ loading: false }))
    if (payload.ok && Array.isArray(payload.data)) {
      const data = payload.data.reduce<IDisplayData[]>((pre, current, curentIndex) => {
        const index = pre.findIndex(item => item.groupId == current[groupField])
        if (index == -1) {
          pre.push({
            groupId: current[groupField] || curentIndex,
            groupName: current[groupField],
            groupDesc: groupDescField ? current[groupDescField] : undefined,
            groupDescColor: groupDescColorField ? current[groupDescColorField] : undefined,
            groupBg: groupBgColorField ? current[groupBgColorField] : undefined,
            groupPadding: groupPaddingField ? current[groupPaddingField] : undefined,
            groupBorderColor: groupBorderColorField ? current[groupBorderColorField] ?? undefined : undefined,
            groupBorderSize: groupBorderSizeField ? current[groupBorderSizeField] ?? undefined : undefined,
            progressType: progressTypeField ? current[progressTypeField] : undefined,
            progressPosition: progressPositionField ? current[progressPositionField] : undefined,
            progressColor: progressColorField ? current[progressColorField] : undefined,
            progressValue: progressField ? current[progressField] ?? undefined : undefined,
            labels: [{
              labelName: current[labelNameField],
              labelValue: (valueFormulaField && current[valueFormulaField]) ? deepCalcFn(current[valueFormulaField], items ?? []) : current[labelValueField],
              labelNameColor: labelNameColorField ? current[labelNameColorField] : undefined,
              labelIcon: iconField ? current[iconField] : undefined,
              labelIconColor: iconColorField ? current[iconColorField] : undefined,
              labelDesc: labelDescField ? current[labelDescField] : undefined,
              labelFontSize: labelFontSizeField ? current[labelFontSizeField] : undefined,
              labelFontColor: labelFontColorField ? current[labelFontColorField] : undefined,
              linkUrl: linkUrlField ? current[linkUrlField] : undefined,
              linkId: linkPageField ? current[linkPageField] : undefined,
              linkTitle: linkTitleField ? current[linkTitleField] : undefined,
              linkData: current
            }]
          })
        } else {
          pre[index].labels.push({
            labelName: current[labelNameField],
            labelValue: (valueFormulaField && current[valueFormulaField]) ? deepCalcFn(current[valueFormulaField], items ?? []) : current[labelValueField],
            labelNameColor: labelNameColorField ? current[labelNameColorField] : undefined,
            labelIcon: iconField ? current[iconField] : undefined,
            labelIconColor: iconColorField ? current[iconColorField] : undefined,
            labelDesc: labelDescField ? current[labelDescField] : undefined,
            labelFontSize: labelFontSizeField ? current[labelFontSizeField] : undefined,
            labelFontColor: labelFontColorField ? current[labelFontColorField] : undefined,
            linkUrl: linkUrlField ? current[linkUrlField] : undefined,
            linkId: linkPageField ? current[linkPageField] : undefined,
            linkTitle: linkTitleField ? current[linkTitleField] : undefined,
            linkData: current
          })
        }
        return pre
      }, [])
      // console.log(data)
      this.setState({ data }, this.getProgressWidth)
    }
  }

  getProgressWidth = () => {
    const rowElement = this.rowRef.current
    if (rowElement?.children && rowElement.children.length > 0) {
      const colElement = rowElement.children.item(0) as HTMLDivElement
      if (colElement) {
        this.setState({ progressWidth: colElement.clientHeight * 0.75 }, this.getColHeight)
      }
    }
  }

  getColHeight = () => {
    const rowElement = this.rowRef.current
    if (rowElement?.children && rowElement.children.length > 1) {
      if (rowElement.clientHeight > 0) {
        this.setState({ mostHigh: 0 }, () => {
          let maxHeight = 0
          for (let i = 0; i < rowElement.children.length; i++) {
            const colElement = rowElement.children.item(i) as HTMLDivElement
            if (colElement) {
              maxHeight = Math.max(maxHeight, colElement.clientHeight)
            }
          }
          this.setState({ mostHigh: maxHeight })
        })
      }
    }
  }

  handleLink(label: ILabel) {
    if (label.linkUrl) {
      const id = 'Matrix' + (label.linkId ?? Date.now().toString())
      const url = tokenize(label.linkUrl, label.linkData ?? {})
      this.props.env.onPageLink?.(id, id, label.linkTitle ?? label.labelName, url, undefined)
    }
  }

  componentDidUpdate(preProps: DataDisplayProps) {
    if (!isEqual(preProps.items ?? [], this.props.items ?? [])) {
      this.getData()
    }
  }

  render() {
    const { groupRowNum, groupWidth, columnNum, columnWidth, labelDirection, classnames: cx } = this.props
    const { data, loading, mostHigh, progressWidth } = this.state
    const isTile = data.every(item => item.groupName == null)

    return (
      <div className={`data-display`} >
        <Spin className='data-display-spin' spinning={loading} />
        <Empty style={{ margin: '16px 0', visibility: loading ? 'hidden' : undefined, display: data.length > 0 ? 'none' : undefined }} description="暂无数据" />
        <div ref={this.rowRef} style={{ gap: 8, display: 'grid', gridTemplateColumns: groupWidth > 0 ? `repeat(auto-fill, minmax(${groupWidth}px, 1fr))` : `repeat(${groupRowNum}, 1fr)` }}>
          {data.map(item => {
            const { groupId, groupName, groupDesc, groupDescColor, groupBg, groupPadding, groupBorderColor, groupBorderSize, labels, progressType, progressPosition, progressColor, progressValue } = item
            const hasProgress = progressType != undefined && progressPosition != undefined
            const progressOnLeftOrRight = progressPosition == 0 || progressPosition == 1
            return (
              <div
                key={groupId}
                className={`data-display-item ${cx(groupBg, groupBorderColor)}`}
                style={{ minHeight: mostHigh, borderWidth: groupBorderSize || 0, padding: groupPadding }}
              >
                {(groupName?.trim() || groupDesc?.trim()) && (
                  <div className='data-display-item-group'>
                    <span className='group-title'>{groupName}</span>
                    <span className={`group-desc ${cx(groupDescColor)}`}>{groupDesc}</span>
                  </div>
                )}
                <div style={{
                  display: hasProgress ? 'flex' : undefined,
                  alignItems: hasProgress && progressOnLeftOrRight ? 'center' : undefined,
                  flexDirection: hasProgress ? progressOnLeftOrRight ? 'row' : 'column' : undefined
                }}
                >
                  {(hasProgress && (progressPosition == 0 || progressPosition == 2)) &&
                    <Progress
                      size='small'
                      width={progressWidth}
                      type={progressType == 0 ? 'circle' : 'line'}
                      status='normal'
                      percent={progressValue}
                      strokeWidth={10}
                      strokeColor={colorObj[progressColor ?? ''] ?? progressColor}
                      strokeLinecap='butt'
                      style={{ marginRight: progressPosition == 0 ? 16 : undefined, marginBottom: progressPosition == 2 ? 8 : undefined }}
                      format={() => labelDirection == 0 ?
                        (progressValue != undefined ? progressValue : 0) + '%' :
                        <div style={{ fontSize: progressType == 0 ? 14 : 12 }}>{(progressValue != undefined ? progressValue : 0)}%</div>}
                    />
                  }
                  <div
                    className={`data-display-item-labels item-${item.groupId}`}
                    style={{
                      display: 'grid',
                      gap: 12,
                      gridTemplateColumns: columnWidth > 0 && !isTile ? `repeat(auto-fill, minmax(${columnWidth}px, 1fr))` : `repeat(${isTile ? 1 : columnNum}, 1fr)`
                    }}
                  >
                    {labels.map((label, index) => {
                      const hidden = isEmpty(label.labelIcon) && isEmpty(label.labelName) && isEmpty(label.labelValue) && isEmpty(label.labelDesc)
                      const labelContent = (
                        <>
                          <div className={`sub-title ${cx(label.labelNameColor)}`} style={{ display: isEmpty(label.labelName) ? 'none' : undefined }}>{label.labelName}</div>
                          <div
                            className={`title ${cx(label.labelFontColor)} ${label.linkUrl ? 'link' : ''}`}
                            style={{ fontSize: label.labelFontSize }}
                            onClick={() => this.handleLink(label)}
                          >
                            {label.labelValue}
                          </div>
                        </>
                      )
                      return (
                        <div
                          key={index}
                          className={labelDirection == 0 ? 'data-display-item-labels-item' : 'data-display-item-vertical-labels-item'}
                          style={{ display: hidden ? 'none' : 'block' }}>
                          <div className='wrap'>
                            <div className={(labelDirection == 0 && label.labelDesc) ? 'left' : undefined}>
                              {label.labelIcon && <Icon className={cx(label.labelIconColor)} icon={label.labelIcon} style={{ fontSize: 38 }} symbol />}
                            </div>
                            <div className='right'>
                              {labelDirection == 0 ? <div className='data-display-item-labels-item-header'>{labelContent}</div> : labelContent}
                              {label.labelDesc && <div className='desc'>{label.labelDesc.split('<br/>').map((str, index) => <p key={index}>{str}</p>)}</div>}
                            </div>
                          </div>
                        </div>
                      )
                    })}
                  </div>
                  {(hasProgress && (progressPosition == 1 || progressPosition == 3)) &&
                    <Progress
                      size='small'
                      width={progressWidth}
                      type={progressType == 0 ? 'circle' : 'line'}
                      status='normal'
                      percent={progressValue}
                      strokeWidth={10}
                      strokeColor={colorObj[progressColor ?? ''] ?? progressColor}
                      strokeLinecap='butt'
                      style={{ marginLeft: progressPosition == 1 ? 16 : undefined, marginTop: progressPosition == 3 ? 8 : undefined }}
                      format={() => labelDirection == 0 ?
                        (progressValue != undefined ? progressValue : 0) + '%' :
                        <div style={{ fontSize: progressType == 0 ? 14 : 12 }}>{(progressValue != undefined ? progressValue : 0)}%</div>
                      }
                    />
                  }
                </div>
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}

@Renderer({ type: 'data-display' })
export class DataDisplayRenderer extends DataDisplay {

  static contextType = ScopedContext;
  constructor(props: DataDisplayProps, context: IScopedContext) {
    super(props);
    const scoped = context;
    scoped.registerComponent(this);
  }

  componentWillUnmount() {
    const scoped = this.context as IScopedContext;
    scoped.unRegisterComponent(this);
  }

  receive(values: any) {
    return super.getData(values);
  }

}
