/* @jsxImportSource vue */
import APage from '@/components/a-page/APage.vue'
import styles from '@/components/a-page/a-table-page/a-table-page.module.styl'
import {
  ElButton,
  ElDropdown,
  ElDropdownItem,
  ElDropdownMenu,
  ElForm,
  ElLoading,
  ElMessageBox,
  ElPagination,
  ElPopconfirm,
  ElScrollbar,
  ElTable,
  ElTableColumn,
  ElTooltip,
  type TableColumnCtx,
  type TableProps,
} from 'element-plus'
import {
  defineComponent,
  withDirectives,
  withModifiers,
} from 'vue'
import type { R } from '@/common/utils/http-util.ts'
import {
  elIcons,
  type ElIconType,
} from '@/common/element/element.ts'
import type { FormProps } from 'element-plus/es/components/form/src/form'
import Utils, { type ResetAble } from '@/common/utils'
import Strings from '@/common/utils/strings.ts'
import { saveFile } from '@/common/app'
import Colls from '@/common/utils/colls.ts'
import type { SetupContext } from '@vue/runtime-core'
import Types from '@/common/utils/types.ts'
import {
  type IconName,
  iconNames,
} from '@/components/a-icon/iconfont.ts'
import AIcon from '@/components/a-icon/AIcon.vue'
import type { DefaultRow } from 'element-plus/es/components/table/src/table/defaults'


interface ColumnScopeType<T> {
  row: T,
  column: TableColumnCtx,
  $index: number
}

interface TableActionType<T> {
  icon?: ElIconType | IconName
  type?: 'text' | 'default' | 'primary' | 'success' | 'warning' | 'info' | 'danger'
  tooltip: string | ((data: ColumnScopeType<T>) => string)
  loading?: boolean
  show?: (data: ColumnScopeType<T>) => boolean
  action: (data: ColumnScopeType<T>) => Promise<boolean> | void
  confirm?: {
    title: string | ((data: ColumnScopeType<T>) => string)
    width?: string
    confirmButtonText?: string
    cancelButtonText?: string
  }
}

interface ActionColumnType<T> {
  /**
   * 列宽，默认：80px
   */
  width: number
  foldLimit: number
  /**
   * 按钮列表
   */
  tableActions: TableActionType<T>[]
}

interface ToolType {
  icon?: ElIconType
  type?: 'text' | 'default' | 'primary' | 'success' | 'warning' | 'info' | 'danger'
  label?: string
  action: () => void
}

interface ToolBarType {
  leftTools: ToolType[]
  rightTools: Required<Omit<ToolType, 'type' | 'label'>>[]
}

interface TablePropsType<T extends DefaultRow, F extends object> extends Omit<TableProps<T>, 'data' | 'headerRowClassName' | 'cellClassName' | 'context'> {
  treeLoad?: (param: F, row: T, expanded: any, resolve?: (data: T[]) => void) => void
  /**
   * 操作列配置
   */
  actionColumn: ActionColumnType<T>
}

interface FormPropsType<P, T extends DefaultRow> {
  /**
   * 分页函数
   * @param param 查询条件
   */
  paging: (param: P) => Promise<R<G.PageResult<T>>>
  list?: (param: P) => Promise<R<T[]>>
  /**
   * 导出函数
   * @param param 查询条件
   */
  export?: (param: P) => Promise<R<{ content: Blob, filename: string }>>
  /**
   * 表单默认值
   */
  defaultData: P
  /**
   * 高级查询表单配置
   */
  highForm: Partial<Omit<FormProps, 'labelWidth'>> & {
    /**
     * 标签宽度，单位：px，默认：90px
     */
    labelWidth: number,
    /**
     * 输入框宽度，单位：px，默认：200px
     */
    contentWidth: number,
    /**
     * 行间隔，单位：px，默认：10px
     */
    rgap: number,
    /**
     * 列间隔，单位：px，默认：10px
     */
    cgap: number
  }
  /**
   * 简单查询表单配置
   */
  simpleForm: Partial<FormProps> & {
    /**
     * 列数，默认：1
     */
    colCount: number,
    /**
     * 输入框宽度，单位：px，默认：200px
     */
    contentWidth: number,

    /**
     * 行间隔，单位：px，默认：10px
     */
    // rgap: number,
    /**
     * 列间隔，单位：px，默认：10px
     */
    // cgap: number
  }
}

/**
 * 页布局配置
 */
interface PageLayoutType {
  /**
   * 查询表单高度，传入数字时，单位：fr，传入字符串时不会而外添加单位，，默认：1fr
   */
  searchFormHeight: number | string
  /**
   * 数据表格高度，传入数字时，单位：fr，传入字符串时不会而外添加单位，，默认：9fr
   */
  dataListHeight: number | string
  showHighForm: boolean
  enableHighForm: boolean
}

interface ATablePageType<P extends object, T extends DefaultRow> {
  /**
   * 页布局配置
   */
  pageLayout: PageLayoutType
  /**
   * 查询表单配置
   */
  searchForm: FormPropsType<P, T>
  /**
   * 工具栏配置
   */
  toolBar: ToolBarType
  /**
   * 表格配置
   */
  table: TablePropsType<T, P>
}

function buildSearchForm<P, T extends DefaultRow>(searchForm: DeepPartial<FormPropsType<P, T>> = {}) {
  if (searchForm.defaultData == null) {
    searchForm.defaultData = {} as DeepPartial<P>
  }
  if (searchForm.highForm == null) {
    searchForm.highForm = {
      labelWidth: 90,
      contentWidth: 200,
      rgap: 10,
      cgap: 10,
    }
  }

  if (searchForm.highForm.labelWidth == null) searchForm.highForm.labelWidth = 90
  if (searchForm.highForm.contentWidth == null) searchForm.highForm.contentWidth = 200
  if (searchForm.highForm.rgap == null) searchForm.highForm.rgap = 10
  if (searchForm.highForm.cgap == null) searchForm.highForm.cgap = 10
  if (searchForm.simpleForm == null) {
    searchForm.simpleForm = {
      contentWidth: 200,
      colCount: 1,
      // rgap: 10,
      // cgap: 10,
    }
  }
  if (searchForm.simpleForm.colCount == null) searchForm.simpleForm.colCount = 1
  if (searchForm.simpleForm.contentWidth == null) searchForm.simpleForm.contentWidth = 200
  // if (searchForm.simpleForm.rgap == null) searchForm.simpleForm.rgap = 10
  // if (searchForm.simpleForm.cgap == null) searchForm.simpleForm.cgap = 10
  return searchForm
}

function buildPageLayout(pageLayout: DeepPartial<PageLayoutType> = {}) {
  if (pageLayout.searchFormHeight == null) {
    pageLayout.searchFormHeight = 1
  }

  if (pageLayout.dataListHeight == null) {
    pageLayout.dataListHeight = 9
  }

  if (pageLayout.showHighForm == null) {
    pageLayout.showHighForm = false
  }

  if (pageLayout.enableHighForm == null) {
    pageLayout.enableHighForm = true
  }

  return pageLayout
}

function buildTable<P extends object, T extends object>(table: DeepPartial<TablePropsType<T, P>> = {}) {
  if (table.actionColumn == null) {
    table.actionColumn = {
      width: 80,
      foldLimit: 3,
      tableActions: [],
    }
  }
  if (table.actionColumn.tableActions == null) {
    table.actionColumn.tableActions = []
  }
  for (let tableAction of table.actionColumn.tableActions) {
    if (tableAction.confirm != null) {
      if (tableAction.confirm.cancelButtonText == null) tableAction.confirm.cancelButtonText = '否'
      if (tableAction.confirm.confirmButtonText == null) tableAction.confirm.confirmButtonText = '是'
      if (tableAction.confirm.width == null) tableAction.confirm.width = '180'
    }
    if (tableAction.type == null) {
      tableAction.type = 'primary'
    }
  }
  if (table.actionColumn.foldLimit == null) {
    table.actionColumn.foldLimit = 3
  }

  if (table.actionColumn.width == null) {
    table.actionColumn.width = Math.min(table.actionColumn.tableActions.length, table.actionColumn.foldLimit) * 50 + 10
  }

  if (table.emptyText == null) {
    table.emptyText = '暂无数据'
  }
  if (table.rowKey == null) {
    table.rowKey = 'id'
  }

  return table
}

function buildToolBar(toolBar: DeepPartial<ToolBarType> = {}) {
  if (toolBar.leftTools == null) toolBar.leftTools = []
  for (let leftTool of toolBar.leftTools) {
    if (leftTool.type == null) {
      leftTool.type = 'primary'
    }
  }
  if (toolBar.rightTools == null) toolBar.rightTools = []

  return toolBar
}

export function buildTablePageProps<P extends G.PageParam, T extends DefaultRow>({pageLayout, searchForm, toolBar, table}: DeepPartial<ATablePageType<P, T>>) {
  return reactive({
    pageLayout: buildPageLayout(pageLayout),
    searchForm: buildSearchForm(searchForm),
    toolBar: buildToolBar(toolBar),
    table: buildTable(table),
  })
}

const component = defineComponent(
  <P extends G.PageParam, T extends DefaultRow>(props: ATablePageType<P, T>, {slots, expose}: SetupContext) => {
    const formData = Utils.resetAble(reactive<P>({
      ...(props.searchForm.defaultData),
      current: 1,
      size: 20,
    })) as ResetAble<P>
    const tableData = Utils.resetAble(reactive<T[]>([])) as ResetAble<T[]>

    const totalCount = ref(0)
    const loading = ref<boolean>(false)
    const showHighForm = ref<boolean>(props.pageLayout.showHighForm)

    const doSearch = () => {
      loading.value = true
      if (props.searchForm.paging != null) {
        props.searchForm.paging(formData.$clone() as P)
          .then(res => {
            totalCount.value = res.data?.total ?? 0
            const records = res.data?.records ?? ([] as T[])
            tableData.$reset(records)
          })
          .finally(() => {
            loading.value = false
          })
        return
      } else if (props.searchForm.list != null) {
        props.searchForm.list(formData.$clone() as P)
          .then(res => {
            const records = res.data ?? ([] as T[])
            tableData.$reset(records)
          })
          .finally(() => {
            loading.value = false
          })
        return
      }
    }
    expose({doSearch, tableData})
    const showHighFormHandle = () => {
      showHighForm.value = !showHighForm.value
      formData.$reset()
    }
    const doExport = () => {
      if (props.searchForm.export == null) {
        return
      }
      loading.value = true
      saveFile(props.searchForm.export(formData.$clone() as P))
        .finally(() => {
          loading.value = false
        })
    }
    const rowAction = (data: ColumnScopeType<T>, action: TableActionType<T>) => {
      if (action.loading != null) {
        action.loading = true
      }
      const result = action.action(data)
      if (result instanceof Promise) {
        result
          .then(reload => {
            if (reload) {
              doSearch()
            }
          })
          .finally(() => {
            if (action.loading != null) {
              action.loading = false
            }
          })
      }
    }

    function doReset() {
      formData.$reset()
      doSearch()
    }

    const highFormCssParam = computed(() => {
      const labelWidth = props.searchForm.highForm.labelWidth
      const contentWidth = props.searchForm.highForm.contentWidth
      return {
        '--item-min-width': (labelWidth + contentWidth) + 'px',
        '--rgap': props.searchForm.highForm.rgap + 'px',
        '--cgap': props.searchForm.highForm.cgap + 'px',
      }
    })

    const toolBarRightCssParam = computed(() => {
      const colCount = props.searchForm.simpleForm.colCount
      const contentWidth = props.searchForm.simpleForm.contentWidth
      const btnCount = props.toolBar.rightTools.length + (props.searchForm.export == null ? 0 : 1) + (props.pageLayout.enableHighForm ? 1 : 0)
      return {
        '--form-item-count': colCount,
        '--form-item-width': contentWidth + 'px',
        // '--rgap': props.searchForm.simpleForm.rgap + 'px',
        // '--cgap': props.searchForm.simpleForm.cgap + 'px',
        '--btn-count': btnCount,
      }
    })

    const pageCssParam = computed(() => {
      const dataListHeight = props.pageLayout.dataListHeight
      const searchFormHeight = props.pageLayout.searchFormHeight
      return {
        '--data-list-height': Types.isString(dataListHeight) ? dataListHeight : dataListHeight + 'fr',
        '--search-form-height': Types.isString(searchFormHeight) ? searchFormHeight : searchFormHeight + 'fr',
      }
    })
    const actionColumnBtnRender = (scope: ColumnScopeType<T>, tableAction: TableActionType<T>) => {
      let elIcon: any | undefined = undefined
      if (iconNames.includes(tableAction.icon as IconName)) {
        elIcon = <AIcon name={tableAction.icon as IconName}/>
      } else if (Object.keys(elIcons).includes(tableAction.icon as unknown as string)) {
        elIcon = elIcons[tableAction.icon as ElIconType]
      }
      const tooltipTxt = tableAction.tooltip == null ? '' : (typeof tableAction.tooltip === 'function' ? tableAction.tooltip(scope) : tableAction.tooltip)
      if (!Strings.isBlank(tooltipTxt)) {
        return (<ElTooltip content={tooltipTxt} placement="top" enterable={false}>
          <ElButton
            icon={elIcon}
            loading={tableAction.loading}
            type={tableAction.type}
            class={'icon-btn'}
            onClick={tableAction.confirm == null ? () => rowAction(scope, tableAction) : undefined}
            plain/>
        </ElTooltip>)
      }
      return (<ElButton
        icon={elIcon}
        loading={tableAction.loading}
        type={tableAction.type}
        class={'icon-btn'}
        onClick={tableAction.confirm == null ? () => rowAction(scope, tableAction) : undefined}
        text/>)
    }
    const actionColumnRender = () => {
      const actionColumn = props.table.actionColumn
      if (Colls.isEmpty(actionColumn.tableActions)) return (<></>)
      return (<ElTableColumn width={actionColumn.width + 'px'} fixed="right" label="操作" prop="tableAction">
        {{
          default: (scope: ColumnScopeType<T>) => {
            const len = actionColumn.tableActions.filter(it => it.show == null || it.show(scope)).length
            let btns: any[]
            if (len <= actionColumn.foldLimit) {
              btns = (actionColumn.tableActions
                  .filter(it => (it.show == null ? true : it.show(scope)))
                  .map((tableAction, i) => (tableAction.confirm != null ?
                    (<ElPopconfirm
                      key={'action-btn-' + i}
                      cancel-button-text={tableAction.confirm.cancelButtonText}
                      confirm-button-text={tableAction.confirm.confirmButtonText}
                      title={typeof tableAction.confirm.title === 'function' ? tableAction.confirm.title(scope) : tableAction.confirm.title}
                      width={tableAction.confirm.width}
                      cancel-button-type="primary"
                      confirm-button-type="danger"
                      onConfirm={() => rowAction(scope, tableAction)}>
                      {{
                        reference: () => (<div>
                          {actionColumnBtnRender(scope, tableAction)}
                        </div>),
                      }}
                    </ElPopconfirm>) : actionColumnBtnRender(scope, tableAction)))
              )
            } else {
              btns = (actionColumn.tableActions
                  .filter(it => (it.show == null ? true : it.show(scope)))
                  .filter((_, i) => i < actionColumn.foldLimit - 1)
                  .map((tableAction, i) => (tableAction.confirm != null ?
                    (<ElPopconfirm
                      key={'action-btn-' + i}
                      cancel-button-text={tableAction.confirm.cancelButtonText}
                      confirm-button-text={tableAction.confirm.confirmButtonText}
                      title={typeof tableAction.confirm.title === 'function' ? tableAction.confirm.title(scope) : tableAction.confirm.title}
                      width={tableAction.confirm.width}
                      cancel-button-type="primary"
                      confirm-button-type="danger"
                      onConfirm={() => rowAction(scope, tableAction)}>
                      {{
                        reference: () => (<div>
                          {actionColumnBtnRender(scope, tableAction)}
                        </div>),
                      }}
                    </ElPopconfirm>) : actionColumnBtnRender(scope, tableAction)))
              )
              btns.push(<ElDropdown onCommand={(cmd) => {
                const tableAction = actionColumn.tableActions[cmd]
                if (tableAction.confirm != null) {
                  ElMessageBox.confirm(
                    typeof tableAction.confirm.title === 'function' ? tableAction.confirm.title(scope) : tableAction.confirm.title,
                    '操作提示',
                    {
                      confirmButtonText: tableAction.confirm.confirmButtonText,
                      cancelButtonText: tableAction.confirm.cancelButtonText,
                      confirmButtonType: 'primary',
                      cancelButtonType: 'danger',
                      type: 'warning',
                    },
                  )
                    .then(() => {
                      rowAction(scope, tableAction)
                    })
                    .catch(() => {
                    })
                  return
                }
                rowAction(scope, tableAction)
              }}>
                {{
                  default: () => (<ElButton icon={elIcons.More} class={[ 'icon-btn', styles.moreIcon ]} text/>),
                  dropdown: () => (<ElDropdownMenu>
                    {
                      actionColumn.tableActions
                        .filter(it => (it.show == null ? true : it.show(scope)))
                        .filter((_, i) => i >= actionColumn.foldLimit - 1)
                        .map((it, i) => {
                          let elIcon: any | undefined = undefined
                          if (iconNames.includes(it.icon as IconName)) {
                            elIcon = <AIcon name={it.icon as IconName}/>
                          } else if (Object.keys(elIcons).includes(it.icon as unknown as string)) {
                            elIcon = elIcons[it.icon as ElIconType]
                          }
                          return (
                            <ElDropdownItem key={'action-btn-' + i} command={i + 2} icon={elIcon}>
                              {typeof it.tooltip === 'function' ? it.tooltip(scope) : it.tooltip}
                            </ElDropdownItem>
                          )
                        })
                    }
                  </ElDropdownMenu>),
                }}
              </ElDropdown>)
            }
            return (<div class={'action-btn'}>
              {btns}
            </div>)
          },
        }}
      </ElTableColumn>)
    }
    const sortChangeHandler = ({prop, order}: { prop: string, order: 'ascending' | 'descending' | null }) => {
      formData.orders = order == null ? undefined : (prop + ':' + (order == 'ascending' ? 'asc' : 'desc'))
      doSearch()
    }
    onMounted(doSearch)
    return () => (<APage class={[ styles.tablePage, showHighForm.value ? '' : styles.folder ]} style={pageCssParam.value}>
      <div class={styles.searchFormWrapper} style={highFormCssParam.value}>
        {props.pageLayout.enableHighForm ? (<ElScrollbar>
          {/*@ts-ignore*/}
          <ElForm class={styles.searchForm} onSubmit={withModifiers(doSearch, [ 'prevent' ])} labelWidth={props.searchForm.highForm.labelWidth + 'px'}>
            {
              slots.highFormItem?.(formData)
            }
            <button style="display: none" type="submit"/>
          </ElForm>
        </ElScrollbar>) : <></>}
      </div>
      <div class={styles.dataList}>
        <div class={styles.toolBar}>
          <div class={styles.toolBarLeft}>
            {
              props.toolBar.leftTools.map((tool, i) => (
                <ElButton key={'tool-bar-left-' + i}
                  icon={Strings.isBlank(tool.icon) ? undefined : elIcons[tool.icon!]}
                  type={tool.type}
                  onClick={tool.action}>
                  {tool.label}
                </ElButton>
              ))
            }
          </div>
          <div class={styles.toolBarRight} style={toolBarRightCssParam.value}>
            <ElScrollbar>
              {/*@ts-ignore*/}
              <ElForm onSubmit={withModifiers(doSearch, [ 'prevent' ])}>
                {
                  slots.simpleFormItem?.(formData)
                }
                <button style="display: none" type="submit"/>
              </ElForm>
            </ElScrollbar>
            <div>
              <ElTooltip content="搜索" placement="top">
                <ElDropdown split-button onClick={doSearch} onCommand={(command: string) => {
                  if (command === 'reset') {
                    doReset()
                  }
                }}>
                  {{
                    default: () => (<ElButton icon={elIcons.Search} loading={loading.value} type="default"/>),
                    dropdown: () => (<ElDropdownMenu>
                      <ElDropdownItem icon={elIcons.Refresh} command="reset">重置并刷新</ElDropdownItem>
                    </ElDropdownMenu>),
                  }}
                </ElDropdown>
              </ElTooltip>
              {
                props.toolBar.rightTools.map((tool, i) => (
                  <ElButton key={'tool-bar-right-' + i}
                    icon={elIcons[tool.icon]}
                    onClick={tool.action}/>
                ))
              }
              {
                props.searchForm.export == null ? <></> : (<ElTooltip content="导出" placement="top">
                  <ElButton icon={elIcons.Download} loading={loading.value} type="default" onClick={doExport}/>
                </ElTooltip>)
              }
              {
                props.pageLayout.enableHighForm ? (<ElTooltip
                  content={showHighForm.value ? '关闭高级搜索' : '打开高级搜索'} placement="top">
                  <ElButton class={showHighForm.value ? styles.filterBtnActive : ''} icon={elIcons.Filter} type="default" onClick={showHighFormHandle}/>
                </ElTooltip>) : <></>}
            </div>
          </div>
        </div>
        {
          withDirectives(<ElTable
            data={tableData}
            empty-text={props.table.emptyText}
            row-key={props.table.rowKey}
            ref="dataTable"
            lazy={props.table.treeLoad == null ? undefined : true}
            load={props.table.treeLoad ? ((row, expanded, resolve) => props.table.treeLoad!(formData as P, row, expanded, resolve)) : undefined}
            onExpand-change={props.table.treeLoad ? ((row: any, expandedRows: any) => props.table.treeLoad!(formData as P, row, expandedRows, undefined)) : undefined}
            cell-class-name="table-cell"
            header-row-class-name="table-header"
            class="data-table"
            span-method={props.table.spanMethod}
            onSort-change={sortChangeHandler}
            summary-method={props.table.summaryMethod}
            show-summary={props.table.summaryMethod != null}
          >
            {
              slots.columns?.()
            }
            {
              actionColumnRender()
            }
          </ElTable>, [ [ ElLoading.directive, loading.value ] ])
        }
        {props.searchForm.paging != null ? <ElPagination
          current-page={(formData as G.PageParam).current}
          page-size={(formData as G.PageParam).size}
          onUpdate:current-page={(val) => (formData as G.PageParam).current = val}
          onUpdate:page-size={(val) => (formData as G.PageParam).size = val}
          hide-on-single-page={false}
          page-sizes={[ 10, 20, 50, 100, 500 ]}
          teleported={false}
          total={totalCount.value}
          background={true}
          layout="total, prev, pager, next, sizes, jumper"
          onChange={doSearch}/> : <></>}
        {
          slots?.default?.()
        }
      </div>
    </APage>)
  },
  {
    name: 'ATablePage',
    props: [ 'pageLayout', 'searchForm', 'toolBar', 'table' ],
  },
)

export interface ATablePageInstance<T extends DefaultRow = DefaultRow> extends InstanceType<typeof component> {
  doSearch: () => void
  tableData: T[]
}

export default component
