import { TableUI, DetailOptionValues, DetailTableValues, PrintSchemaUI, PropertyItem, PropertyUI } from "./types";
import { combineCell, getCellValue } from "../../../../../store/utils/commonTableFunction";
import { isVisible } from "../../../../../utils/helper";
import { RendererEnv } from "../../../../../env";
import { calcFn } from "../../../../../utils/utils";

enum FlowNodeData {
    seq = '序号',
    nodeName = '审批节点',
    nodeType = '签核类型',
    nodeUser = '审批人',
    nodeStatus = '审批结果',
    nodeEndTime = '审批时间',
    remarkComment = '审批意见',
    assignComment = '加签意见',
    attachments = '附件'
}

interface IFormSchema {
    type: 'form'
    body: (IPropertySchema | IFieldSetSchema | IServiceSchema)[]
    [key: string]: any
}

interface IPropertySchema {
    type: 'property'
    column: number
    items?: PropertyItem[]
    title?: string
}

interface IFieldSetSchema {
    type: 'fieldSet'
    title: string
    body: IPropertySchema[]
}

interface IServiceSchema {
    type: 'service'
    schemaApi: any
}

interface ITabsSchema {
    type: 'tabs'
    tabs: ITabsItemSchema[]
}

interface ITabsItemSchema {
    type: 'tabsItem'
    title: string,
    body: IServiceSchema[]
}

interface ICrudSchema {
    type: 'crud'
    name: string
    api: any
    aliasTitle: string
    data: object[]
    columns: object[]
}

type ISchema = IFormSchema | IServiceSchema | ITabsSchema | ITabsItemSchema | IFieldSetSchema | IPropertySchema | ICrudSchema

const createPropertyElement = (ui: PropertyUI, options: DetailOptionValues & DetailTableValues, border: boolean) => {
    const { column = 3, body: rows, data } = ui
    const table = document.createElement('table')
    if (rows.length === 0) return table
    table.style.borderCollapse = 'collapse'
    table.style.tableLayout = 'fixed'
    table.style.border = border ? `${Math.max(2, options.borderWidth ?? 2)}px solid black` : 'none'
    table.style.wordBreak = options.numberBreak ? 'break-all' : 'break-word'
    table.style.fontSize = `${options.contentFontSize}px`
    table.style.fontFamily = options.contentFontFamily
    table.setAttribute('width', '100%')

    const tbody = table.createTBody()
    for (const row of rows) {
        const tr = document.createElement('tr')
        for (let index = 0; index < row.length; index++) {
            const item = row[index]
            if (item.content.type === 'input-table-field') {
                const td = document.createElement('td')
                td.colSpan = column * 2
                td.style.padding = '0'
                td.style.border = 'none'
                td.style.borderStyle = 'hidden'

                const tableData = getValue(item.content, data)
                const label = item.label ?? ''
                const tableValues = options.detailTableValues
                const setting = tableValues.find(value => value.name === item.content.name)
                if (setting?.isPrint === false) {
                    continue
                }
                const fieldColumns = item.content.columns?.filter((column: any) => setting ? setting.columns.find(v => v.name === column.name)?.printType === 'all' : true) ?? []
                if (item.content.tableLayout === 'horizontal') {
                    if (item.content.showIndex === true) fieldColumns.unshift({ type: 'index', name: 'index', label: '' })
                    const datas = combineCell(tableData.map((data: any) => ({ ...data, rowSpans: {} })), fieldColumns.filter((column: any) => column.combined))
                    if (label.length > 0) {
                        const div = document.createElement('div')
                        if (setting) {
                            div.style.fontSize = `${setting.fontSize}px`
                            div.style.fontFamily = setting.fontFamily
                        }
                        div.textContent = label
                        td.appendChild(div)
                    }
                    const fieldTable = document.createElement('table')
                    fieldTable.style.borderStyle = 'hidden'
                    fieldTable.style.borderCollapse = 'collapse'
                    fieldTable.style.fontSize = `${setting?.fontSize ?? 12}px`
                    fieldTable.style.fontFamily = setting?.fontFamily ?? '宋体'
                    fieldTable.setAttribute('width', '100%')
                    //字段表格thead
                    const fieldThead = fieldTable.createTHead()
                    const fieldTr = document.createElement('tr')
                    for (const fieldColumn of fieldColumns) {
                        const fieldTh = document.createElement('th')
                        fieldTh.style.textAlign = fieldColumn.align
                        fieldTh.textContent = fieldColumn.label
                        fieldTr.appendChild(fieldTh)
                    }
                    fieldThead.appendChild(fieldTr)
                    //字段表格tbody
                    const fieldTbody = fieldTable.createTBody()
                    datas.forEach((data, index) => {
                        const fieldTr = document.createElement('tr')
                        for (const fieldColumn of fieldColumns) {
                            const rowspan = data.rowSpans[fieldColumn.name]
                            if (rowspan === 0) continue
                            const fieldTd = document.createElement('td')
                            fieldTd.style.textAlign = fieldColumn.align
                            fieldTd.rowSpan = rowspan || 1
                            fieldTd.textContent = fieldColumn.type === 'index' ? index + 1 : getValue(fieldColumn, data)
                            fieldTr.appendChild(fieldTd)
                        }
                        fieldTbody.appendChild(fieldTr)
                    })
                    if (Array.isArray(item.content.affixRow)) {
                        const summaryTr = document.createElement('tr')
                        for (const fieldColumn of fieldColumns) {
                            const target = item.content.affixRow.find((field: any) => field.name === fieldColumn.name)
                            const td = document.createElement('td')
                            if (target) {
                                const value = calcFn(target.formula?.toLocaleLowerCase(), target.name, datas)
                                td.textContent = (target.rule ? target.rule + ':' : '') + getCellValue(value, { ...fieldColumn, pristine: fieldColumn })
                            }
                            summaryTr.appendChild(td)
                        }
                        fieldTable.appendChild(summaryTr)
                    }
                    td.appendChild(fieldTable)

                } else if (item.content.tableLayout === 'vertical') {
                    tableData.forEach((data: any, index: number) => {
                        const div = document.createElement('div')
                        if (setting) {
                            div.style.fontSize = `${setting.fontSize}px`
                            div.style.fontFamily = setting.fontFamily
                        }
                        div.style.padding = `${options.rowPadding}px ${options.colPadding}px`
                        div.textContent = label + `${index + 1}`
                        td.appendChild(div)
                        const rows = buildPropertyUI({ type: 'property', column, items: fieldColumns.map((column: any) => ({ label: column.label, span: 1, content: { ...column, value: getValue(column, data) } })) }, data)
                        const table = createPropertyElement({ type: 'property', column, body: rows, data }, options, false)
                        table.style.fontSize = `${setting?.fontSize ?? 12}px`
                        table.style.fontFamily = setting?.fontFamily ?? '宋体'
                        table.style.borderStyle = 'hidden'
                        td.appendChild(table)
                    })
                }
                tr.appendChild(td)
            } else {
                const isLastFillCell = item.span == 1 && row.length != column && item.label === row[row.length - 1]?.label
                const th = document.createElement('th')
                th.style.width = `${options.labelWidthPercent / column}%`
                th.textContent = item.label?.replace(/\n|\r|<[^>]*>/g, '') ?? ''
                const td = document.createElement('td')
                td.colSpan = isLastFillCell ? column * 2 - (index * 2 + 1) : item.span * 2 - 1
                td.textContent = getValue(item.content, data)
                tr.appendChild(th)
                tr.appendChild(td)
            }
        }
        tbody.appendChild(tr)
    }
    return table
}

const createTableElement = (ui: TableUI, options: DetailOptionValues & DetailTableValues) => {
    const { columns, data } = ui.body
    const setting = options.detailTableValues.find(value => value.name === ui.name)
    const table = document.createElement('table')
    if (setting?.isPrint === false) { return table }
    table.style.borderCollapse = 'collapse'
    table.style.border = `${Math.max(2, options.borderWidth ?? 2)}px solid black`
    table.style.wordBreak = options.numberBreak ? 'break-all' : 'break-word'
    table.style.fontSize = `${setting?.fontSize ?? options.contentFontSize}px`
    table.style.fontFamily = setting?.fontFamily ?? options.contentFontFamily
    table.setAttribute('width', '100%')

    const thead = table.createTHead()
    const tr = document.createElement('tr')
    const tableColumns = columns.filter(column => setting ? setting.columns.find(v => v.name === column.name)?.printType === 'all' : true)
    for (const column of tableColumns) {
        const th = document.createElement('th')
        th.textContent = column.label
        th.style.textAlign = column.align
        tr.appendChild(th)
    }
    thead.appendChild(tr)
    const tbody = table.createTBody()
    data.forEach(item => {
        const tr = document.createElement('tr')
        for (const column of tableColumns) {
            const td = document.createElement('td')
            td.textContent = getValue(column, item)
            td.style.textAlign = column.align
            tr.appendChild(td)
        }
        tbody.appendChild(tr)
    })
    return table
}

const createLabelElement = (label: string) => {
    const labelDiv = document.createElement('div')
    labelDiv.textContent = label
    labelDiv.style.backgroundColor = 'gray'
    labelDiv.style.padding = '6px'
    labelDiv.style.color = 'white'
    labelDiv.style.fontSize = 'large'
    labelDiv.style.marginBottom = '12px'
    labelDiv.style.width = '20%'
    labelDiv.style.minWidth = '80px'
    labelDiv.style.fontWeight = 'bold'
    return labelDiv
}

const getValue = (schema: any, data: any) => {
    const value = data[schema.name] ?? schema.value
    const types = ['mapping', 'static-mapping', 'number', 'static-number']
    if (types.includes(schema.type)) {
        return getCellValue(value, { ...schema, type: schema.type.replace('static-', ''), pristine: schema })
    }
    if (schema.type === 'lion-upload') {
        const tempValue = data[schema.name]
        return tempValue?.info?.map((item: any) => item.name).join(',')
    }
    if (schema.type === 'switch') {
        return value === schema.trueValue ? schema.onText : schema.offText
    }
    if (schema.type === 'input-table-field') {
        return data[schema.name] ?? []
    }
    if (schema.type === 'flow-user') {
        return value?.userName ?? ''
    }
    if (schema.type === 'flow-attachment') {
        return value?.map((item: any) => item.name).join(',')
    }
    return data[schema.name]
}

export const createHtml = (schema: { form: PrintSchemaUI[], flow?: TableUI }, options: DetailOptionValues & DetailTableValues) => {
    const body = document.createElement('body')
    const { form, flow } = schema
    // console.log(form, flow)
    if (flow) {
        const labelDiv = createLabelElement('申请详情')
        body.appendChild(labelDiv)
    }
    form.forEach(item => {
        if (item.label) {
            const div = document.createElement('div')
            div.textContent = item.label
            div.style.fontSize = `${options.contentFontSize}px`
            div.style.fontFamily = options.contentFontFamily
            div.style.marginTop = '6px'
            div.style.marginBottom = '6px'
            body.appendChild(div)
        }
        if (item.type === 'property') {
            const table = createPropertyElement(item, options, true)
            body.appendChild(table)
        } else if (item.type === 'table') {
            const table = createTableElement(item, options)
            body.appendChild(table)
        }
    })
    if (flow) {
        const labelDiv = createLabelElement('审批详情')
        labelDiv.style.marginTop = '20px'
        body.appendChild(labelDiv)
        const flowTable = createTableElement(flow, options)
        body.appendChild(flowTable)
    }

    return `<!DOCTYPE html>
  <html>
      <head>
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      </head>
      <style>
          body {
            width: 100%;
          }
          td, th {
              border: ${options.lineWidth}px solid;
              font-weight: normal;
              padding: ${options.rowPadding}px ${options.colPadding}px;
          }
          div {
              padding: ${options.rowPadding}px ${options.colPadding}px;
          }
      </style>
      ${body.outerHTML}
  </html>`
}

export const findLabel = (name: string, object: any): { label: string } | undefined => {
    if (typeof object == 'object' && object != null) {
        if (Array.isArray(object)) {
            for (const item of object) {
                const target = findLabel(name, item)
                if (target) return target
            }
        }
        if (Object.prototype.hasOwnProperty.call(object, 'label') && Object.prototype.hasOwnProperty.call(object, 'content')) {
            const content = object['content']
            if (content['name'] === name) {
                return object
            }
        }
        for (const key in object) {
            if (Object.prototype.hasOwnProperty.call(object, key)) {
                const value = object[key];
                const target = findLabel(name, value)
                if (target) return target
            }
        }
    }
    return undefined
}

export const getDetailTableValues = async (schema: IFormSchema, formData: object, fetcher: RendererEnv['fetcher']): Promise<DetailTableValues['detailTableValues']> => {
    const schemas = await computed(schema, formData, fetcher)
    return schemas.map(schema => {
        if (schema.type === 'property') {
            if (Array.isArray(schema.items)) {
                return schema.items.filter(item => {
                    return item.content.type === 'input-table-field' && Array.isArray(item.content.columns)
                }).map(item => {
                    return {
                        name: item.content.name,
                        isPrint: true,
                        label: item.label ?? '',
                        fontSize: 12,
                        fontFamily: '宋体',
                        columns: item.content.columns.map((column: any) => {
                            return {
                                name: column.name,
                                title: column.label,
                                align: column.align ?? 'left',
                                printType: 'all'
                            }
                        })
                    }
                })
            }
        } else {
            return {
                name: schema.name,
                isPrint: true,
                label: schema.aliasTitle ?? '',
                fontSize: 12,
                fontFamily: '宋体',
                columns: schema.columns.filter((column: any) => column.type !== 'operation').map((column: any) => {
                    return {
                        name: column.name,
                        title: column.label,
                        align: column.align ?? 'left',
                        printType: 'all' as any
                    }
                })
            }
        }
        return []
    }).flat()
}

export const buildPropertyUI = (property: IPropertySchema, data: object) => {
    const items = property.items?.filter(item => isVisible(item, data)) ?? []
    const column = property.column ?? 3
    const rows: PropertyItem[][] = [];
    let row: PropertyItem[] = []
    let columnLeft = column;
    let index = 0;
    for (const item of items) {
        index = index + 1;
        const span = Math.min(item.span || 1, column);
        columnLeft = columnLeft - span;
        const rowItem: PropertyItem = {
            label: item.label,
            content: item.content,
            span: span,
        };
        if (columnLeft >= 0) {
            row.push(rowItem);
        } else {
            rows.push(row);
            columnLeft = column - span;
            row = [rowItem];
        }
        if (index === items.length) {
            rows.push(row);
        }
    }
    return rows
}

const computed = async (node: ISchema | ISchema[], formData: object, fetcher: RendererEnv['fetcher'], title?: string) => {
    if (Array.isArray(node)) {
        const result: (IPropertySchema | ICrudSchema)[] = []
        for (const item of node) {
            const schema = await computed(item, formData, fetcher, title)
            result.push(...schema)
        }
        return result
    } else {
        if (node.type === 'service') {
            try {
                const { data } = await fetcher(node.schemaApi, formData ?? {})
                return await computed(data, formData, fetcher, title)
            } catch (error) {
                return []
            }
        }
        if (node.type === 'tabs') {
            return await computed(node.tabs.map(tab => ({ ...tab, type: 'tabsItem' })), formData, fetcher, title)
        }
        if (node.type === 'form') {
            try {
                if (node.initApi) {
                    const { data } = await fetcher(node.initApi, formData ?? {})
                    return await computed(node.body, data, fetcher, title)
                }
                return await computed(node.body, formData, fetcher, title)
            } catch (error) {
                return await computed(node.body, formData, fetcher, title)
            }
        }
        if (node.type === 'fieldSet' || node.type === 'tabsItem') {
            return await computed(node.body, formData, fetcher, node.title)
        }
        if (node.type === 'property') {
            if (title) node.title = title
            return [node]
        }
        if (node.type === 'crud') {
            return [node]
        }
        return []
    }
}

export const buildPrintFormSchema = async (formSchema: IFormSchema, formData: object, fetcher: RendererEnv['fetcher']) => {
    const dealSchema = async (schema: IPropertySchema | ICrudSchema) => {
        if (schema.type === 'property') {
            await Promise.all(schema.items?.map(async (item) => {
                if (item.content.type === 'static-mapping' && item.content.source) {
                    const { ok, data } = await fetcher(item.content.source, formData).catch(() => ({ ok: false, data: null }))
                    ok && (item.content.map = data)
                } else if (item.content.type === 'input-table-field') {
                    await Promise.all(item.content.columns?.map(async (column: any) => {
                        if (column.type === 'mapping' && column.source) {
                            const { ok, data } = await fetcher(column.source, formData).catch(() => ({ ok: false, data: null }))
                            ok && (column.map = data)
                        }
                    }) ?? [])
                }
            }) ?? [])
        } else if (schema.type === 'crud') {
            const { ok, data } = await fetcher(schema.api, { perPage: 30, page: 1 }).catch(() => ({ ok: false, data: null }))
            ok && (schema.data = data.items)
            await Promise.all(schema.columns?.map(async (column: any) => {
                if (column.type === 'mapping' && column.source) {
                    const { ok, data } = await fetcher(column.source, formData).catch(() => ({ ok: false, data: null }))
                    ok && (column.map = data)
                }
            }) ?? [])
        }
        return schema
    }
    const schemas = await computed(formSchema, formData, fetcher)
    return await Promise.all(schemas.map(async schema => { return await dealSchema(schema) }))
}

export const buildPrintFlowSchema = (printInfo?: { processNodeList: Record<keyof typeof FlowNodeData, any>[] }): TableUI | undefined => {
    const nodeList = printInfo?.processNodeList
    if (Array.isArray(nodeList)) {
        const hasAssignComment = nodeList.some((item) => !!item.assignComment)
        const columns = Object.entries(FlowNodeData).filter(([name]) => hasAssignComment ? true : name !== 'assignComment').map(value => {
            const [name, label] = value
            if (name === 'nodeType') {
                return { label, name, type: 'mapping', map: { 0: '或签', 1: '会签', 2: '依次审批', 3: '系统签核', 4: '加签' } }
            }
            if (name === 'nodeStatus') {
                return { label, name, type: 'mapping', map: { submit: '提交申请', waiting: '待签核', doing: '签核中', revert: '撤销', refuse: '拒绝', pass: '通过', return: '退回' } }
            }
            if (name === 'nodeUser') {
                return { label, name, type: 'flow-user' }
            }
            if (name === 'attachments') {
                return { label, name, type: 'flow-attachment' }
            }
            return { label, name }
        })
        return {
            type: 'table',
            name: 'flow-detail',
            body: { columns, data: nodeList }
        }
    }
    return undefined
}

export function chunkArray(arr: any[], size: number) {
    const result = [];
    for (let i = 0; i < arr.length; i += size) {
        result.push(arr.slice(i, i + size));
    }
    return result;
}

