import React, { useEffect, useMemo, useState, useRef, CSSProperties, useCallback } from 'react';

import { LayoutModeEnum } from '../../../constants/table-enum';
import {Action} from '../../../types';
import { ClassNamesFn } from '../../../theme'

import Icon from '../../../components/icons';
import Button from '../../../components/Button';
import {default as Tabs, Tab} from '../../../components/Tabs';
import Modal from '../../../components/Modal';
import TooltipWrapper from '../../../components/TooltipWrapper'


import { tokenize } from '../../../utils/tpl-builtin'

import winResetEvent from '../../../utils/event'
import {
  createObject,
} from '../../../utils/helper';

import Avatar from './TableCardAvatar'
import { TaskIcon, SubTaskIcon, ArrowRightIcon, DeleteIcon } from './icon'
import { HeadLoading, CardLoading, ContentLoading } from './TableRowInfoLoading'
import { getWConfig, gRowFields } from './helper'
import { statusStyle, priorityStyle, LINK_TYPE_ENUM, attachmentEnum } from './const'


interface ItemType {
  label: string;
  name: string;
  type: string;
  key: string;
  children?: ItemType[];
  quickEdit?: boolean;
  quickStatic?: boolean;
  options?: [] | string;
  components?: ItemType[];
}

interface RowInfoType {
  getApi: string//
  saveApi: string//
  addApi: string// 新增子任务
  addLinkApi: string// 新增关联任务
  delLinkApi: string// 删除关联
  mappingApi: string
  layout: any
  layoutSet: any
  minTableWidth?: number | string// 表格最小宽度 数字单位是px  字符串看填写的单位如果没填也默认px (不填默认 TableMinWidth 比TableMinWidth小 也等于 TableMinWidth)
  minWideWidth?: number | string// 宽详情最小宽度 (不填默认 WideMinWidth， 比WideMinWidth小 也等于 WideMinWidth)
  minNarroWidth?: number | string// 窄详情最小宽度(不填默认 NarrowMinWidth， 比NarrowMinWidth小 也等于 NarrowMinWidth)
  wideRowInfoWidth?: number | string// 宽详情默认宽度 （大于等于 minWideWidth，不填则等于 minWideWidth）
  narrowRowInfoWidth?: number | string// 窄详情默认宽度 （大于等于 minNarroWidth，不填则等于 minNarroWidth）
  assembly?: React.FC<Pick<TableRowInfoProps, "primaryFieldKey" | "primaryFieldValue" | "refresh">> // 传入自定义组件时直接渲染
  mapping?:any
  rowFields?:any
  sub_Name?:string //子名称
}

interface TableRowInfoProps {
  classnames: ClassNamesFn
  layoutMode: LayoutModeEnum
  showHead?: boolean// 是否显示详情头 默认显示
  rowInfo: RowInfoType// 详情基础组件和排版配置
  primaryFieldKey: string// 详情字段key
  primaryFieldValue: string// 详情字段value
  paddingLeft?: number// 页面的左侧padding
  paddingRight?: number// 页面的右侧padding
  paddingBottom?: number// 页面的底部padding
  baseURL: string// env.baseURL

  addSchema: () => any;// 新增按钮schema
  delSchema: () => any;// 删除按钮schema
  onChange: (data: object) => void// 将改变的属性向上传递
  onChangePrimaryField: (id: string) => void// 切换id
  refresh: (data: object) => void// 刷新外层
  resolveVariable: (source: string, mapping: any) => any// 查找mapping

  copy: any
  render: (...reset: any) => any
  fetcher: (api: any, data?: any, options?: any) => any// 请求接口
}

interface renderTitleLinkProps{
  ids: string[];
  copy: (txt: string) => any;
  onClick: (id: string) => void;
  sub_Name?: string;
}


function RenderTitleLink(props: renderTitleLinkProps){
  let { copy, ids, onClick, sub_Name } = props

  return (
    <>
      {
        ids.map((id: string, i: number) => {

          if(i === ids.length - 1){
            return (
              <React.Fragment key={i}>
              <SubTaskIcon />
              <TooltipWrapper
                placement={'top'}
                tooltip={{title: '点击复制 ID+标题+链接'}}
                container={null}
                trigger={['hover']}
                rootClose={true}
                key={id}
              >
                <span key={id} onClick={() => copy && copy(``)}>#{id || ''}
                {
                  sub_Name?` - ${sub_Name}`:''
                }
                </span>
              </TooltipWrapper>
              </React.Fragment>
            )
          }

          return (
            <React.Fragment key={i}>
              <TaskIcon />
                <a onClick={() => onClick && onClick(id)}>#{id || ''}</a>
              <ArrowRightIcon />
            </React.Fragment>
          )
        })
      }
    </>
  )
}


function SubTaskList({data, onClickItem, mappingVariable, renderDelBtn, delTooltip}: {
  data: any,
  delTooltip: string,
  onClickItem: (id: string) => void
  mappingVariable: (field: string, fieldVal: string) => string
  renderDelBtn: (itemData: any) => React.ReactNode
}){

  return (
    data.map((v: any)=>{
      let status = mappingVariable('STATUS', v.STATUS)
      let priority = mappingVariable('PRIORITY', v.PRIORITY)
      let assignee = mappingVariable('ASSIGNEE', v.ASSIGNEE)

      let statusStyl: CSSProperties = {}
      if(status){
        statusStyl = {
          color: statusStyle[v.STATUS],
          border: `1px solid ${statusStyle[v.STATUS]}`
        }
      }

      return (
        <div className='ui-item' key={v.ISSUE_ID} onClick={() => {
          onClickItem(v.ISSUE_ID)
        }}>
          <div>{v.ISSUE_ID}</div>
          {
            priority? <div className='ui-tag-key-field' style={priorityStyle[v.PRIORITY] || {}}>{ priority }</div>: null
          }
          {
            assignee? <div className='ui-tag-key-field'>{ assignee }</div>: null
          }
          <div className='summary-column'>
            <div className='task-description one-line--ellipsis'>
              <div className='prefix-container'><SubTaskIcon /></div>
              <span>{v.ISSUE_TITLE}</span>
            </div>
          </div>

          {
            status? <div className="status-column" style={statusStyl}>{ status }</div>: null
          }

          <TooltipWrapper
            placement={'top'}
            tooltip={{title: delTooltip}}
            container={null}
            trigger={['hover']}
            rootClose={true}
          >
            <div className="delete-btn-column" onClick={(e) => e.stopPropagation()}><DeleteIcon />
            { renderDelBtn(v) }
            </div>
          </TooltipWrapper>


        </div>
      )
    })
  )
}

const TableRowInfoContainer: React.FC<any> = (props) => {
  const { classnames: cx, rowInfo, paddingLeft=0, paddingRight=0, layoutModeUsed, setMode } = props;

  let wConfig = getWConfig(rowInfo, paddingLeft, paddingRight)
  
  const [sashClicked, setSashClicked] = useState<boolean>(false)// 是否触发拖拽
  const [minimum, setMinimum] = useState<boolean>(false)// 超过了拖拽的最小宽度
  const [maximum, setMaximum] = useState<boolean>(false)// 超过了拖拽的最大宽度
  const [rowInfoWidth, setWidth] = useState<number>(0)// 整个组件的宽度

  useEffect(() => {
    let fnId = winResetEvent.addEvent(() => {
      wConfig = getWConfig(rowInfo, paddingLeft, paddingRight)
    })
    return () => {
      winResetEvent.removeEvent(fnId)
    }
  }, [])
  

  useEffect(() => {
    setWidth(calcWidth())
  }, [props.layoutMode])

  

  function calcWidth(){
    let {
      winWidth,// body 宽度
      winUsableWidth,// 实际可用宽度
      minTableWidth,// 表格最小宽度
      minWideWidth,// 宽详情最小宽度
      minNarroWidth,// 窄详情最小宽度
      wideRowInfoWidth,// 宽详情默认宽度
      narrowRowInfoWidth,// 窄详情默认宽度
    } = wConfig

    let w = 0;
    if(props.layoutMode === LayoutModeEnum.wide){
      w = wideRowInfoWidth
    }else{
      w = narrowRowInfoWidth
    }

    // 如果 详情 + 最小表格 超过了 屏幕宽度
    // 如果 最小详情 + 最小表格 依然超过了 屏幕宽度
    // 那么 表格的我们不动 详情只能变得更小了 小到可能低于最小详情
    if(w + minTableWidth > winUsableWidth || minNarroWidth + minTableWidth > winUsableWidth){
      w = winUsableWidth - minTableWidth
    }

    // 如果 模式时宽详情 而宽度确小于 最小宽详情 切换内部模式
    if(w < minWideWidth && props.layoutMode === LayoutModeEnum.wide){
      setMode(LayoutModeEnum.narrow)
    }
    
    return w
  }

  function handleMouseMove(e: any){
    let x = e.clientX || e.pageX;

    let {
      winWidth,// body 宽度
      winUsableWidth,// 实际可用宽度
      minTableWidth,// 表格最小宽度
      minWideWidth,// 宽详情最小宽度
      minNarroWidth,// 窄详情最小宽度
      wideRowInfoWidth,// 宽详情默认宽度
      narrowRowInfoWidth,// 窄详情默认宽度
    } = wConfig


    let point = winWidth - minWideWidth - paddingRight

    if(sashClicked){
      setMinimum(false)
      setMaximum(false)
      if(x <= minTableWidth + paddingLeft){
        setMinimum(true)
      }else if(x >= winWidth - minNarroWidth - paddingRight){
        setMaximum(true)
      }else{
        let mode = x < point? LayoutModeEnum.wide: LayoutModeEnum.narrow;
        mode !== layoutModeUsed && setMode(mode)
        setWidth(winWidth - x - paddingRight)
      }
    }
  }

  

  return (
    <div className={cx('Row-detail')} style={{width: rowInfoWidth}}>
      <div className={cx('Row-detail-sash-container')}>
        <div className={cx('code-sash-mask vertical', {maskShow: sashClicked, minimum, maximum})}
        onMouseMove={handleMouseMove}
        onMouseUp={() => setSashClicked(false)}
        ></div>
        <div className={cx('code-sash vertical', {active: sashClicked})} onMouseDown={() => setSashClicked(true)} onMouseUp={() => setSashClicked(false)}></div>
      </div>

      {props.children}
    </div>
  )
}

export default (props: TableRowInfoProps) => {
  const { classnames: cx, render, rowInfo } = props;
  const { saveApi } = rowInfo

  const renderMap = new Map([
    ['details', <RenderDetails />],// 详情
    ['progress', <RenderProgress />],// 周期与进度
    ['subWork', <RenderSubWork />],// 子任务
    ['associated', <RenderAssociated />],// 关联
    ['test', <RenderTest />],// 测试
    ['files', <RenderFiles />],// 文件
    ['assembly', <TabAssembly />],// 自定义组件
  ]);

  const [loading, setLoading] = useState(true)
  const [pageErr, setError] = useState<string>('')
  const dataBackup = useRef<any>(null)// 详情接口数据备份
  const [detailData, setDetailData] = useState<any>({})// 详情接口数据
  const [mappingData, setMappingData] = useState<any>({})// mapping
  const [fields, setFields] = useState<any>({})// 所有字段schema
  const [dialogOpen, setDialogOpen] = useState<boolean>(false)//
  const [dialogData, setDialogData] = useState<any>({})//
  const [action, setAction] = useState<any>({})//

  const [rowInfoConLoading, setRowInfoConLoading] = useState(false)// 可不要
  const [activeKey, setActiveKey] = useState<any>('details');// 可不要

  const [insideMode, setMode] = useState<'' | LayoutModeEnum>('')// 内部的模式 优先用这个


  useEffect(() => {
    setMode('')
  }, [props.layoutMode])

  useEffect(() => {
    // 获取数据
    fetchRowInfo({
      [props.primaryFieldKey]: props.primaryFieldValue
    })
    setError('')
    setActiveKey('details')
  }, [props.primaryFieldValue])


  // 布局模式 优先用内部的
  const layoutModeUsed = useMemo(() => {
    return insideMode || props.layoutMode
  }, [props.layoutMode, insideMode])

  const rowInfoHead = useMemo(() => {
    const { showHead=true } = props;

    if(showHead){
      return (
        <div className={cx('Row-detail-head')}>
          {
            loading? <HeadLoading></HeadLoading>: renderHead() || null
          }
        </div>
      )
    }
    return null
  }, [detailData, loading])

  const Record = useMemo(() => {
    let { render, rowInfo, paddingBottom } = props;
    if(rowInfo.assembly) return null
    let layoutSet = rowInfo.layoutSet
    let recordProps: any = {
      primaryFieldKey: props.primaryFieldKey,
      primaryFieldValue: props.primaryFieldValue,
      fetcher: props.fetcher,
      baseURL: props.baseURL,
    }
    if(paddingBottom){
      recordProps.footStyle = {bottom: paddingBottom}
    }
    return render('record', layoutSet.record, recordProps)
  }, [props.primaryFieldKey, props.primaryFieldValue])

  const rowInfoContent = useMemo(() => {
    let { rowInfo } = props;
    let layoutSet = rowInfo.layoutSet
    if(rowInfo.assembly) return null
    return (
      <div className={cx('Row-detail-content')}>
        <div className={cx('RowDetail', `RowDetail--${layoutModeUsed}`)}>
          {renderHanderTitle()}

          <div className={cx('RowDetail-container')}>
            <div className={cx('RowDetail-container-left')}>
              {
                loading? <ContentLoading />: (
                  layoutModeUsed === LayoutModeEnum.wide
                  ? renderTabs()
                  : layoutSet.tabs.map((item: ItemType, i: number) => {
                      return (
                        <span key={i}>
                          {renderMap.get(item.key)}
                        </span>
                      );
                    })
                )
              }
            </div>

            <div className={cx('RowDetail-container-right')}>
              {Record}
            </div>
          </div>

        </div>
      </div>
    )
  }, [detailData, loading, activeKey, layoutModeUsed, insideMode, rowInfoConLoading])



  function changePrimaryField(id: string){
    const { onChangePrimaryField} = props;
    onChangePrimaryField(id)
    setActiveKey('details')
  }

  function renderHead(){
    const { render, copy, fetcher, onChangePrimaryField, refresh, delSchema, primaryFieldValue} = props;

    const delBtnSchema = delSchema();
    delBtnSchema.noProps = !0
    delBtnSchema.onAction = async (e: any, action: any, data: any) => {

      let api = tokenize(action.api, data)

      const json = await fetcher(api)
      if(json.ok){
        refresh({})
        setTimeout(() => {
          onChangePrimaryField('')
        }, 1000)
      }
    }

    const { parentIssueIds, __sub_Name } = detailData;
    let ids = Array.isArray(parentIssueIds)? parentIssueIds.slice(): [];
    ids = ids.reverse().concat(primaryFieldValue)

    return (
      <div className={cx('Row-detail-head__topbar')}>
        <div className={cx('Row-detail-head__nav')}>
          <RenderTitleLink copy={copy} ids={ids} onClick={changePrimaryField} sub_Name={__sub_Name}/>
        </div>
        <div className={cx('Row-detail-head__actions')}>
          <div className={cx('action-item')}>
          {
            props.rowInfo.addApi && addTaskBtn(props.rowInfo.addApi, '新建子工作项', <Icon icon="sub-work" />)
          }
          </div>
          {
            render('actions-more', {
              "type": "dropdown-button",
              "level": "link",
              "label": "更多",
              "icon": "fa fa-ellipsis-h",
              "hideCaret": true,
              "buttons": [
                delBtnSchema,
              ]
            }, {
              key: 'dropdown-actions-more',
              data: {
                ...detailData
              },
            })
          }
        </div>
      </div>
    )
  }

  function mappingVariable(field: string, fieldVal: string){
    if(!fieldVal) return null
    let { resolveVariable } = props;
    let source = fields[field]?.source || null
    if(source){
      let options: any[] = resolveVariable(source, mappingData);
      if(Array.isArray(options) && options.length){
        let answer = options.filter(v=> v.value === fieldVal)
        if(answer.length === 1){
          return answer[0]?.label || ''
        }
      }
    }

    return null
  }

  // 新建按钮
  function addTaskBtn(api: string, label: string, icon: React.ReactNode){
    let { render, primaryFieldKey, primaryFieldValue, addSchema } = props;

    let _addSchema = null
    try{
      _addSchema = JSON.parse(JSON.stringify(addSchema()))
    }catch(e){}
    let btnExtra = null

    if(_addSchema){
      _addSchema.dialog.body.api = tokenize(api, {
        [primaryFieldKey]: primaryFieldValue,
        PROJECT_ID: detailData.PROJECT_ID
      })
      _addSchema.dialog.title = label

      btnExtra = (
        <>
          <Button tag='div' className={cx(`Row-detail-action`)} level="text">
            {icon}
            <span>{label}</span>
            {
              render('subTask', _addSchema, {
                data: mappingData? createObject(mappingData): null,
                onAction(e: any, action: any, data: any){
                  setDialogOpen(true)
                  setAction(action)
                  setDialogData(createObject(data.__super, {
                    items: data.items,
                    selectedItems: data.selectedItems,
                    unSelectedItems: data.unSelectedItems
                  }))
                }
              })
            }
          </Button>
        </>
      );
    }
    return btnExtra
  }

  function addLinkBtn(label: string, icon: React.ReactNode){
    let { linkIssues, requireIssues } = detailData
    let ids, type: LINK_TYPE_ENUM;
    if(label === '关联工作项'){
      ids = linkIssues
      type = LINK_TYPE_ENUM.ISSUE_LINK
    }else{
      ids = requireIssues
      type = LINK_TYPE_ENUM.REQUIRE_LINK
    }
    let value = ids.map((v: any)=> v.ISSUE_ID);
    let linkSchame = {
      "title": label,
      "label": "",
      "type": "tabs-transfer-picker",
      "name": "transfer",
      "value": value.join(','),
      "sortable": false,
      "selectable": '切换项目',
      "selectMode": "tree",
      "pickerSize": "lg",
      "menuTpl": "<div class='flex justify-between'><span style='overflow: hidden; text-overflow: ellipsis; white-space: nowrap;'>(${value}) - ${label}</span></div>",
      "valueTpl": "<div class='flex justify-between'><span style='overflow: hidden; text-overflow: ellipsis; white-space: nowrap;'>(${PROJECT_NAME} - ${value}) - ${label}</span></div>",
      "source": `get:/issues/api/linkOptions?TYPE=${type}&ISSUE_ID=${props.primaryFieldValue}&PROJECT_ID=${detailData.PROJECT_ID}`,
    }

    return (
      <Button tag='div' className={cx(`Row-detail-action`)} level="text">
        {icon}
        <span>{label}</span>
        {
          render('subTask', linkSchame, {
            data: mappingData? createObject(mappingData): null,
            onChange(val: string){// 只做新增 不做删除
              if(val){
                function getAdd(arr: string[], arr2: string[]){
                  return arr2.filter(v => !~arr.indexOf(v))
                }
                let addIds = getAdd(value, val.split(','));
                if(addIds.length){
                  saveLink(addIds, type)
                }
              }

            },
          })
        }
      </Button>
    )
  }

  // 按钮组件
  function EventComponents(props: any) {
    const {label, btnExtra, conExtra, emptyTxt, borderBottom=true} = props;
    return (
      <section className={cx('RowDetail-event-components')}>
        <div className={cx('RowDetail-components__header')}>
          <div className={cx('RowDetail-components__title')}>{label}</div>
          <div className={cx('RowDetail-components__btns')}>{btnExtra}</div>
        </div>
        {!conExtra ? (
          <div className={cx('ui-empty')}> {emptyTxt || '暂无' + label}</div>
        ) : (
          <div className={cx('ui-file', {
            borderBottom: borderBottom
          })}>
          {conExtra}
          </div>
        )}
      </section>
    );
  }

  // 详情
  function RenderDetails() {
    const { rowInfo } = props;
    const { layoutSet, } = rowInfo

    const details = layoutSet.tabs.filter(
      (v: ItemType) => v.key === 'details'
    )[0].components;

    return (
      <span>
        {details?.map((v: any, i: number): React.ReactNode => {
          if (v.children[0] == 'ISSUE_DESC') {
            let textarea = v.children[0]
            return (
              <div
                key={i}
                className={cx(
                  'RowDetail-module',
                  `RowDetail-module-${layoutModeUsed}`
                )}
              >
                <div className={cx('RowDetail-title')}>{v.label}</div>
                <div className={cx('RowDetail-module-textarea')}>
                  <TypeComponents name={textarea}/>
                </div>
              </div>
            );
          }
          if (v.children) {
            return (
              <div
                key={i}
                className={cx(
                  'RowDetail-module',
                  `RowDetail-module-${layoutModeUsed}`,
                  'column-2'
                )}
              >
                <div className={cx('RowDetail-title')}>{v.label}</div>
                {v.children.map((item: any, ind: number) => {
                  if(!fields[item]) return null;
                  return (
                    <div className={cx('RowDetail-module-item',{"is-block":fields[item]?.block})} key={ind}>
                      <label>{fields[item]?.label|| item}</label>
                      <TypeComponents
                        name={item}
                        className={cx('RowDetail-module-cell')}
                      />
                    </div>
                  );
                })}
                <div className={cx('RowDetail-module-item-divider')}></div>
              </div>
            );
          }
        })}
      </span>
    );
  }

  // 周期与进度
  function RenderProgress() {
    const { rowInfo } = props;
    const { layoutSet, } = rowInfo

    const progress = layoutSet.tabs.filter(
      (v: ItemType) => v.key === 'progress'
    )[0].components;
    return (
      <span>
        <div
          className={cx(
            'RowDetail-module',
            `RowDetail-module-${layoutModeUsed}`,
            'list-module'
          )}
        >
          {progress.map((v: ItemType, i: number) => {
            return (
              <span key={i}>
                <div className={cx('RowDetail-title')}>{v.label}</div>
                {v.children?.map((item:any, ind) => {
                  return (
                    <div key={ind} className={cx('RowDetail-module-item')}>
                      <label>{fields[item]?.label}</label>
                      <TypeComponents
                        name={item}
                        className={cx('RowDetail-module-cell')}
                      />
                    </div>
                  );
                })}
              </span>
            );
          })}
          <div className={cx('RowDetail-module-item-divider')}></div>
        </div>
      </span>
    );
  }

  // 子工作项
  function RenderSubWork() {
    let { subIssues } = detailData
    const btnExtra = addTaskBtn(props.rowInfo.addApi, '新建子工作项', <Icon icon="sub-work" />);
    const { fetcher, refresh } = props;

    return (
      <EventComponents
      label="子工作项"
      borderBottom={false}
      btnExtra={btnExtra}
      conExtra={
        Array.isArray(subIssues) && subIssues.length?
        <SubTaskList data={subIssues} delTooltip="删除子工作项" onClickItem={changePrimaryField} mappingVariable={mappingVariable} renderDelBtn={(record) => {
          const { render, delSchema } = props;
          const delBtnSchema = delSchema();

          return render('del-subtask', {
            ...delBtnSchema,
            confirmText: '当前工作项「${ISSUE_TITLE}」会被删除，此操作不可撤销，是否确定？',
            onAction: async (e: any, action: any, data: any) => {
              let api = tokenize(action.api, data)

              const json = await fetcher(api)
              if(json.ok){
                refresh({})
                setDetailData({
                  ...detailData,
                  subIssues: subIssues.filter((v: any)=> v.ISSUE_ID !== data.ISSUE_ID)
                })
              }
            },
            noProps: !0
          }, {
            data: {
              ...record
            }
          })
        }}/>: null
      }
      />
    )
  }

  // 关联内容
  function RenderAssociated() {
    let iconCss = {width: 16, height: 16}
    const workBtnExtra = (
      <>
        {
          addLinkBtn('关联工作项', <Icon icon="related-work" style={iconCss} />)
        }
        {
          addTaskBtn(props.rowInfo.addLinkApi, '新建关联工作项', <Icon icon="add-related-work" style={iconCss} />)
        }
      </>
    )
    const requireBtnExtra = (
      <>
        {
          addLinkBtn('关联需求', <Icon icon="related-work" style={iconCss} />)
        }
        {
          addTaskBtn(props.rowInfo.addLinkApi, '新建关联需求', <Icon icon="add-related-work" style={iconCss} />)
        }
      </>
    )

    let { linkIssues, requireIssues } = detailData
    const { fetcher, primaryFieldValue } = props;

    return (
      <span>
        <EventComponents
        btnExtra={workBtnExtra}
        label="关联工作项"
        borderBottom={false}
        conExtra={
          Array.isArray(linkIssues) && linkIssues.length?
          <SubTaskList data={linkIssues} delTooltip="移除关联工作项" onClickItem={changePrimaryField} mappingVariable={mappingVariable} renderDelBtn={(record) => {
            const { render, rowInfo } = props;

            return render('del-subtask', {
              actionType: "ajax",
              api: rowInfo.delLinkApi,
              label: "删除",
              level: "danger",
              type: "button",
              confirmText: '确定移除关联工作项「${ISSUE_TITLE}」?',
              noProps: !0,
              onAction: async (e: any, action: any, data: any) => {
                const json = await fetcher(action.api, {
                  ISSUE_ID: data.ISSUE_ID,
                  DEL_RELATE_ID: primaryFieldValue
                })
                if(json.ok){
                  setDetailData({
                    ...detailData,
                    linkIssues: linkIssues.filter((v: any)=> v.ISSUE_ID !== data.ISSUE_ID)
                  })
                }
              },
            }, {
              data: {
                ...record
              }
            })
          }}/>: null
        }/>
        <EventComponents
        btnExtra={requireBtnExtra}
        label="关联需求"
        borderBottom={false}
        conExtra={
          Array.isArray(requireIssues) && requireIssues.length?
          <SubTaskList data={requireIssues} delTooltip="移除关联需求" onClickItem={changePrimaryField} mappingVariable={mappingVariable} renderDelBtn={(record) => {
            const { render, rowInfo } = props;

            return render('del-subtask', {
              actionType: "ajax",
              api: rowInfo.delLinkApi,
              label: "删除",
              level: "danger",
              type: "button",
              confirmText: '确定移除关联需求「${ISSUE_TITLE}」?',
              noProps: !0,
              onAction: async (e: any, action: any, data: any) => {
                const json = await fetcher(action.api, {
                  ISSUE_ID: data.ISSUE_ID,
                  DEL_RELATE_ID: primaryFieldValue
                })
                if(json.ok){
                  setDetailData({
                    ...detailData,
                    requireIssues: requireIssues.filter((v: any)=> v.ISSUE_ID !== data.ISSUE_ID)
                  })
                }
              },
            }, {
              data: {
                ...record
              }
            })
          }}/>: null
        }/>
      </span>
    );
  }

  // 测试情况
  function RenderTest() {
    const testBtnExtra = useMemo(() => {
      return (
        <>
          <Button level="text" tooltip="关联测试计划">
            <Icon icon="related-plan" />
          </Button>
        </>
      );
    }, []);
    const resultsBtnExtra = useMemo(() => {
      return (
        <>
          <Button level="text" tooltip="关联测试计划">
            <Icon icon="related-plan" />
          </Button>
        </>
      );
    }, []);
    const libraryBtnExtra = useMemo(() => {
      return (
        <>
          <Button level="text" tooltip="新建关联用例">
            <Icon icon="add-related-work" />
          </Button>
          <Button level="text" tooltip="关联用例">
            <Icon icon="related-library" />
          </Button>
        </>
      );
    }, []);
    return (
      <span>
        <EventComponents btnExtra={testBtnExtra} label="关联测试计划" />
        <EventComponents btnExtra={resultsBtnExtra} label="关联执行结果" />
        <EventComponents btnExtra={libraryBtnExtra} label="关联用例库用例" />
      </span>
    );
  }

  // 文件
  function RenderFiles() {
    const { rowInfo } = props;
    const { layoutSet, } = rowInfo

    const files = layoutSet.tabs.filter((v: ItemType) => v.key === 'files')[0]
      .components;

    return (
      <span>
        {
          files.map((v:string,i:number)=>{
            return <EventComponents conExtra={ <TypeComponents name={v}/>} label={fields[v]?.label} key={i} />
          })
        }
      </span>
    );
  }

  //自定义组件
  function TabAssembly(){
    const { rowInfo } = props;
    const { layoutSet, } = rowInfo
    const Component = layoutSet.tabs.filter((v: ItemType) => v.key === 'assembly')[0]
      .components;
    if(!Component) return null;
    return (<Component primaryFieldValue={props.primaryFieldValue} refresh={props.refresh} primaryFieldKey={props.primaryFieldKey}/>)
  }

  function renderTabs(){
    let { layoutSet } = props.rowInfo;
    let { subIssues, linkIssues, requireIssues } = detailData
    return (
      <Tabs
        {...layoutSet.layout}
        activeKey={activeKey}
        onSelect={(key: number) => {
          setActiveKey(key);
        }}
      >
        {layoutSet.tabs.map((item: any) => {
          let title = item.title;
          if(item.key === 'subWork' && Array.isArray(subIssues) && subIssues.length){
            title = (
              <span>{title} <span className='tab-badge'>{subIssues.length}</span></span>
            )
          }

          if(item.key === 'associated' && Array.isArray(linkIssues) && linkIssues.length){
            title = (
              <span>{title} <span className='tab-badge'>{linkIssues.length + requireIssues.length}</span></span>
            )
          }

          return (
            <Tab title={title} key={item.key} eventKey={item.key}>
              {renderMap.get(item.key)}
            </Tab>
          );
        })}
      </Tabs>
    );
  }

  function renderHanderTitle(){
    const { rowInfo, resolveVariable } = props;
    let { layoutSet } = rowInfo;

    if(!layoutSet.title && !layoutSet.titCard) return null
    return (
      <div className={cx('RowDetail-header')}>
        {
          loading? <CardLoading horizontal={layoutModeUsed === LayoutModeEnum.wide} />: (
            <>
              <div className={cx('RowDetail-header-main')}>
                <TypeComponents name={layoutSet.title} />
              </div>
              <div
                className={cx(
                  'RowDetail-header-fields',
                )}
              >
                {layoutSet.titCard?.map((v:string, i:number)=> {
                  let titobj:any = {label:v};
                  if(fields[v].source && v){
                    let options = resolveVariable(fields[v].source, mappingData)
                    titobj = options.filter((item:any)=>{
                      return item.value === detailData[v]
                    })[0]
                  }
                  return (
                    <div className={cx('RowDetail-header-fields__field')} key={i}>
                      <div
                        className={cx('RowDetail-header-fields__field_icon')}
                      >
                        {renderTitleCardIcon(v, detailData[v])}
                      </div>
                      <div className={cx('RowDetail-header-fields__field_content')}>
                        <span
                          className={cx('RowDetail__field-title', {
                            'RowDetail__field-title-empty': !detailData[v]
                          })}
                        >
                          {  titobj?.label || '未设置'}
                        </span>
                        <span className={cx('RowDetail__field-subTitle')}>
                          {fields[v]?.label}
                        </span>
                      </div>
                      <TypeComponents
                        name={v}
                        className={cx('RowDetail-header-fields__field_select')}
                      />
                    </div>
                  );
                })}
              </div>
            </>
          )
        }

      </div>
    );
  }

  function renderTitleCardIcon(field: string, val: string){
    if(field === 'PRIORITY'){
      return (
        <div>
          <svg className="ui-icon icon-priority2 " viewBox="0 0 16 16">
            <path d="m8 0c.474 0 .95.18 1.307.539l6.155 6.154c.716.716.716 1.9 0 2.616l-6.155 6.154c-.716.716-1.899.716-2.615 0l-6.155-6.154c-.716-.717-.716-1.9 0-2.616l.096-.077 5.982-5.981.077-.096c.358-.359.833-.539 1.307-.539zm.01 2.069-5.944 5.941 5.934 5.932 5.942-5.941zm.99 7.931v2h-2v-2zm0-6v5h-2v-5z"></path>
          </svg>
        </div>
      )
    }

    if(field === 'STATUS'){
      if(['Backlog', 'Todo', 'Pending'].includes(val)){// 未开始
        return (
          <div style={{background: '#338fe5', color: '#fff'}}>
            <svg className="ui-icon icon-priority2 " viewBox="0 0 16 16">
              <path d="m9.506 2.486 5.48 5.48-5.549 5.548-1.37-1.37 3.175-3.176h-11.242v-1.936h11.277l-3.158-3.159z" fillRule="evenodd"></path>
            </svg>
          </div>
        )
      }else if(['Doing', 'Deving', 'ForTest', 'Testing', 'Deploy', 'ForRelease'].includes(val)){// 进行中
        return (
          <div style={{background: '#f0a100', color: '#fff'}}>
            <svg className="ui-icon icon-priority2 " viewBox="0 0 16 16">
              <path d="m9.48 2.48 5.5 5.5-5.541 5.54-1.375-1.375 4.155-4.156-4.125-4.124zm-4.562 0 5.5 5.5-5.54 5.54-1.376-1.375 3.172-3.173h-6.674v-1.944l6.695-.001-3.162-3.162z" fillRule="evenodd"></path>
            </svg>
          </div>
        )
      }else if(['Done', 'Closed', 'Released'].includes(val)){// 已完成
        return (
          <div style={{background: '#24b47e', color: '#fff'}}>
            <svg className="ui-icon icon-priority2 " viewBox="0 0 16 16">
              <path d="m13.43 3 1.57 1.584-6.809 7.737.006.007-.103.103-1.38 1.569-1.57-1.584-4.144-4.18 1.57-1.583 4.05 4.084z"></path>
            </svg>
          </div>
        )
      }
    }

    if((field === 'ASSIGNEE' || field === 'REPORTER')  && val){
      return (
        <div>
          <Avatar src={`/fss/avatar/${val}`} name={val}/>
        </div>
      )
    }

    return null
  }


  // 按钮组件
  function TypeComponents(props: any) {
    if(Object.keys(detailData).length<1) return null

    const { name, className } = props
    let {descTem, type, label, ...rest} = fields[name] || {};
    if(!type) return null;

    let subProps: any = {}
    if(name === 'ISSUE_TITLE'){
      type = 'textarea'
      subProps.minRows = 1;
      subProps.placeholder = ''
    }
    if (name === "ISSUE_DESC") {
      descTem = detailData[name]

      return (
        render('milkdown', {
          "type": "input-md",
          disabled: false,
          value: descTem,
        }, {
          onChange: (val: string) => {
          },
          onFocus: () => {
          },
          onBlur: (val: string) => {
            if( val != detailData[name]){
              saveRowInfo({[name]: val})
            }
          },
        })
      )
    } else {
      return(
        render(
          name,
          {
            type,
            className,
            ...rest,
            quickEdit: rest.quickEdit??true,
            value: detailData[name],
            popOverContainer: document.body,
          },
          {
            data: mappingData? createObject(mappingData, {
              ...detailData,
            }): detailData,
            ...subProps,
            onChange:function(val: any, _: any, changeImmediately: boolean | undefined){
              if(type != 'input-text' && type != 'textarea' && val != detailData[name] && !rowInfoConLoading){
                if( name === 'IMAGES' || name === 'FILES'){
                  saveAttachment(name, compareFile(detailData[name], val))
                }else{
                  saveRowInfo({[name]: val})
                }
              }
            },
            onBlur:function(e: any){
              const val = e.target.value;
              if( (type == 'input-text' || type == 'textarea') && val != detailData[name]){
                saveRowInfo({[name]: val})
              }
            }
          }
        )
      )
    }
  }

  function handleDialogConfirm(
    values: object[],
    action: Action,
    ctx: any,
    components: Array<any>
  ){
    setDialogOpen(false)
    fetchRowInfo({
      [props.primaryFieldKey]: props.primaryFieldValue
    })
    props.refresh({})
  }

  function handleDialogClose(confirmed = false){
    setDialogOpen(confirmed)
  }

  async function fetchRowInfo(data: any){
    const { fetcher, rowInfo, primaryFieldValue } = props;

    if(rowInfo && rowInfo.getApi){
      setLoading(true)
      const json = await fetcher(rowInfo.getApi, data)
      if(json.ok){
        dataBackup.current = json.data
        let PROJECT_ID = json?.data?.PROJECT_ID
        let url = rowInfo.mappingApi, url2 = ''
        if(PROJECT_ID === 'TODO'){
          url +=`?TODOS=my`
          url2 = `/pages/todo/issues-my/json/rowFields?TODOS=my`;
        }else{
          url +=`?PROJECT_ID=${PROJECT_ID}&ISSUE_CLASS=Issue&id=${primaryFieldValue}`
          url2 = `/pages/project/issues/json/rowFields?PROJECT_ID=${PROJECT_ID}&ISSUE_CLASS=Issue`;
        }

        if(rowInfo.rowFields){
          setFields(gRowFields(rowInfo.rowFields))
        }else{
          const fieldsRes = await fetcher(url2)
          setFields(gRowFields(fieldsRes.data))
        }

        if(rowInfo.mapping){
          setMappingData(rowInfo.mapping)
        }else{
          const mappingRes = await fetcher(url)
          setMappingData(mappingRes.data)
        }
        let sub_Name='' //中文名
        if(rowInfo.sub_Name){
          sub_Name = json.data[rowInfo.sub_Name]|| ''
        }
        
        setDetailData({
          ...json.data,
          __sub_Name: sub_Name
        })
      }else{
        setError(json.msg || '没有查到数据')
      }
      setLoading(false)
    }
  }

  function compareFile(originData: string='', newData: string=''){
    let _origin = originData.split(',').filter(v=> v);
    let _new = newData.split(',').filter(v=> v);
    let type = attachmentEnum.add
    let files: string[] = []
    if(_new.length < _origin.length){
      type = attachmentEnum.remove
      files = _origin.filter(v=> !~_new.indexOf(v))
    }else{
      files = _new.filter(v=> !~_origin.indexOf(v))
    }

    return {
      type,
      files: files.join(','),
      newData
    }
  }

  // 新增关联
  async function saveLink(relateIssues: string[], LINK_TYPE: LINK_TYPE_ENUM){
    const { fetcher, primaryFieldKey, primaryFieldValue } = props;
    try{
      const json = await fetcher(`post:/issues/api/addRelate`, {
        [primaryFieldKey]: primaryFieldValue,
        relateIssues,
        LINK_TYPE
      });

      if(json.ok){
        fetchRowInfo({
          [props.primaryFieldKey]: props.primaryFieldValue
        })
      }
    }catch(e){}
  }
  // 保存附件
  async function saveAttachment(filed: 'IMAGES' | 'FILES', {type, files, newData}: {type: attachmentEnum, files: string, newData: string}){
    if(!files){
      return
    }

    const { fetcher, primaryFieldValue } = props;
    setRowInfoConLoading(true)
    try{
      const json = await fetcher(`${type === attachmentEnum.add? 'post': 'delete'}:/issues/api/issues/${primaryFieldValue}/file`, {
        [filed]: files
      });
      if(json.ok){
        let newDetailData = {
          ...dataBackup.current,
          [filed]: newData
        }
        setDetailData(newDetailData)
        dataBackup.current = newDetailData
      }
    }catch(e){
      setDetailData(dataBackup.current)
    }
    setRowInfoConLoading(false)
  }

  // 保存字段
  async function saveRowInfo(data: any){
    const { fetcher, onChange } = props;

    if(typeof(Object.values(data)[0]) === "undefined"){
      return
    }
    const saveObj = Object.assign({},{
        ISSUE_ID: detailData.ISSUE_ID,
        PROJECT_ID: detailData.PROJECT_ID,
        ...data
    })
    let newDetailData = {
      ...dataBackup.current,
      ...data,
    }

    setRowInfoConLoading(true)
    try{
      const json = await fetcher(saveApi, saveObj);
      if(json.ok){
        dataBackup.current = newDetailData
        onChange && onChange(data)
        setDetailData(newDetailData)
      }
    }catch(e){
      setDetailData(dataBackup.current)
    }
    setRowInfoConLoading(false)
  }

  const Assembly = rowInfo.assembly || null;

  return (
    <TableRowInfoContainer  
    classnames={cx}
    rowInfo={rowInfo}
    layoutModeUsed={layoutModeUsed}
    paddingLeft={props.paddingLeft}
    paddingRight={props.paddingRight}
    layoutMode={props.layoutMode}
    setMode={setMode} 
    >
      <div className={cx('Row-detail-container')}>
        {
          pageErr? <span style={{alignSelf: 'center', marginTop: 100, fontSize: 15}}>{pageErr}</span>: 
          Assembly? <Assembly primaryFieldValue={props.primaryFieldValue} refresh={props.refresh} primaryFieldKey={props.primaryFieldKey}/>: 
          <>
          {rowInfoHead}
          {rowInfoContent}
          </>
        }
      </div>

      {render(
        'dialog',
        {
          ...((action as Action) &&
            ((action as Action).dialog as object)),
          type: 'dialog'
        },
        {
          key: 'dialog',
          data: dialogData,
          onConfirm: handleDialogConfirm,
          onClose: handleDialogClose,
          show: dialogOpen
        }
      )}
    </TableRowInfoContainer>
  )
}