<template>
    <div class="near-realtime-data">
        <div class="view_bot">
            <template v-for="(section, sectionIndex) in tableSections" :key="sectionIndex">
                <vxe-table
                    class="near-realtime-table"
                    border
                    height=""
                    width="100%"
                    :data="[section.row]"
                 v-bind="defaultAttribute"
                    :column-config="tableColumnConfig"
                    :tooltip-config="tableTooltipConfig"
                >
                    <template v-for="(item, colIndex) in section.columns" :key="colIndex">
                        <vxe-colgroup
                            v-if="item.type === 'abc'"
                            align="center"
                            :title="getPrimaryTitle(item.data)"
                            :width="getMetricWidth(section)"
                        >
                            <vxe-column
                                align="center"
                                :field="`v${colIndex}A`"
                                :title="item.data.phaseLabels![0]"
                                :width="getPhaseWidth(section)"
                                :formatter="cellFormatter"
                            ></vxe-column>
                            <vxe-column
                                align="center"
                                :field="`v${colIndex}B`"
                                :title="item.data.phaseLabels![1]"
                                :width="getPhaseWidth(section)"
                                :formatter="cellFormatter"
                            ></vxe-column>
                            <vxe-column
                                align="center"
                                :field="`v${colIndex}C`"
                                :title="item.data.phaseLabels![2]"
                                :width="getPhaseWidth(section)"
                                :formatter="cellFormatter"
                            ></vxe-column>
                        </vxe-colgroup>
                        <vxe-colgroup
                            v-else-if="item.type === 't-multi'"
                            align="center"
                            :title="getPrimaryTitle(item.data)"
                            :width="getMetricWidth(section)"
                        >
                            <vxe-column
                                v-for="(sub, subIndex) in item.data.subItems"
                                :key="subIndex"
                                align="center"
                                :field="`t${colIndex}_${subIndex}`"
                                :title="sub.subTitle"
                                :width="getSubColWidth(section, item.data.subItems!.length)"
                                :formatter="cellFormatter"
                            ></vxe-column>
                        </vxe-colgroup>
                        <vxe-column
                            v-else
                            align="center"
                            :field="`s${colIndex}`"
                            :title="getScalarTitle(item.data)"
                            :width="getMetricWidth(section)"
                            show-overflow
                            :formatter="cellFormatter"
                        ></vxe-column>
                    </template>
                    <vxe-column
                        v-for="emptyIndex in section.emptySlotCount"
                        :key="`empty-${emptyIndex}`"
                        align="center"
                        :width="getMetricWidth(section)"
                    ></vxe-column>
                </vxe-table>
                <div v-if="sectionIndex < tableSections.length - 1" class="table-gap"></div>
            </template>

            <el-empty :style="height" description="暂无数据" v-if="tableSections.length === 0" />
        </div>
    </div>
</template>

<script setup lang="ts">
import { mainHeight } from '@/utils/layout'
import { defaultAttribute } from '@/components/table/defaultAttribute'
import { ref } from 'vue'

const ROW_WITH_GROUP = 4
const ROW_SCALAR_ONLY = 5

const SECONDARY_KEYWORDS = ['正序', '负序', '零序', '无功', '有功', '视在'] as const

interface SubColumn {
    subTitle: string
    value: unknown
}

interface DisplayMetric {
    name: string
    otherName: string
    unit?: string | null
    type: 'abc' | 'scalar' | 't-multi'
    valueA?: unknown
    valueB?: unknown
    valueC?: unknown
    valueM?: unknown
    phaseLabels?: [string, string, string]
    subItems?: SubColumn[]
}

interface MetricItem {
    type: 'abc' | 'scalar' | 't-multi'
    data: DisplayMetric
}

interface TableSection {
    columns: MetricItem[]
    row: Record<string, unknown>
    slotsPerRow: number
    emptySlotCount: number
}

interface RawMetricItem {
    targetId?: string
    name?: string
    unit?: string | null
    phase?: string
    sort?: number
    otherNameSort?: number
    data?: unknown
    otherName?: string
    valueA?: unknown
    valueB?: unknown
    valueC?: unknown
    valueM?: unknown
}

interface NameGroup {
    name: string
    items: RawMetricItem[]
}

const PHASE_GROUPS = [
    { keys: ['A', 'B', 'C'], labels: ['A相', 'B相', 'C相'] as [string, string, string] },
    { keys: ['AB', 'BC', 'CA'], labels: ['AB相', 'BC相', 'CA相'] as [string, string, string] }
]

const height = mainHeight(345)
const tableSections = ref<TableSection[]>([])

const tableColumnConfig = { resizable: false, tooltip: true }
const tableTooltipConfig = { enterable: true }

const getMetricWidth = (section: TableSection) => `${100 / section.slotsPerRow}%`

const getPhaseWidth = (section: TableSection) => `${100 / section.slotsPerRow / 3}%`

const getSubColWidth = (section: TableSection, subCount: number) => `${100 / section.slotsPerRow / subCount}%`

const formatCellValue = (value: unknown): string | number => {
    if (value == null || value === 3.14159) return '/'
    return typeof value === 'number' || typeof value === 'string' ? value : String(value)
}

const cellFormatter = ({ cellValue }: { cellValue: unknown }) => formatCellValue(cellValue)

const buildTitle = (name: string, unit?: string | null) => (unit ? `${name}(${unit})` : name)

const getSharedUnit = (items: RawMetricItem[]) => {
    const units = [...new Set(items.map(item => item.unit ?? null))]
    return units.length === 1 ? units[0] : null
}

const getPrimaryTitle = (item: DisplayMetric) => buildTitle(item.name, item.unit)

const getScalarTitle = (item: DisplayMetric) => buildTitle(item.otherName || item.name, item.unit)

const isNewFormat = (item: RawMetricItem) => item.targetId != null && item.phase != null && 'data' in item

const isGroupedColumn = (item: MetricItem) => item.type === 'abc' || item.type === 't-multi'

const extractSubTitle = (otherName: string) => {
    for (const keyword of SECONDARY_KEYWORDS) {
        if (otherName.includes(keyword)) return keyword
    }
    return otherName
}

const groupByName = (list: RawMetricItem[]): NameGroup[] => {
    const order: string[] = []
    const map = new Map<string, RawMetricItem[]>()

    list.forEach(item => {
        const name = item.name || ''
        if (!map.has(name)) {
            order.push(name)
            map.set(name, [])
        }
        map.get(name)!.push(item)
    })

    return order.map(name => ({ name, items: map.get(name)! }))
}

const buildAbcMetric = (name: string, items: RawMetricItem[]): DisplayMetric => {
    const phaseMap: Record<string, unknown> = {}
    items.forEach(item => {
        if (item.phase != null) phaseMap[item.phase] = item.data
    })

    for (const { keys, labels } of PHASE_GROUPS) {
        if (keys.some(key => phaseMap[key] != null)) {
            return {
                name,
                otherName: items[0].otherName || name,
                unit: items[0].unit ?? null,
                type: 'abc',
                valueA: phaseMap[keys[0]],
                valueB: phaseMap[keys[1]],
                valueC: phaseMap[keys[2]],
                phaseLabels: labels
            }
        }
    }

    return {
        name,
        otherName: items[0].otherName || name,
        unit: items[0].unit ?? null,
        type: 'abc',
        valueA: phaseMap.A ?? phaseMap.AB,
        valueB: phaseMap.B ?? phaseMap.BC,
        valueC: phaseMap.C ?? phaseMap.CA,
        phaseLabels: phaseMap.AB != null ? ['AB相', 'BC相', 'CA相'] : ['A相', 'B相', 'C相']
    }
}

const buildTMetric = (name: string, items: RawMetricItem[]): DisplayMetric => {
    const needSubHeader = items.some(item => (item.otherName || item.name || '') !== name)

    if (!needSubHeader) {
        return {
            name,
            otherName: items[0].otherName || name,
            unit: items[0].unit ?? null,
            type: 'scalar',
            valueM: items[0].data
        }
    }

    const sharedUnit = getSharedUnit(items)

    return {
        name,
        otherName: name,
        unit: sharedUnit,
        type: 't-multi',
        subItems: items.map(item => ({
            subTitle: buildTitle(extractSubTitle(item.otherName || item.name || ''), item.unit),
            value: item.data
        }))
    }
}

const groupToMetric = (group: NameGroup): DisplayMetric => {
    const { name, items } = group
    const phases = items.map(item => item.phase)
    const allT = phases.every(phase => phase === 'T')

    if (allT) return buildTMetric(name, items)

    return buildAbcMetric(name, items)
}

const normalizeOldItem = (item: RawMetricItem): DisplayMetric => {
    const name = item.name || ''
    const otherName = item.otherName || name
    const hasAbc = item.valueA != null || item.valueB != null || item.valueC != null

    if (hasAbc) {
        return {
            name,
            otherName,
            unit: item.unit ?? null,
            type: 'abc',
            valueA: item.valueA,
            valueB: item.valueB,
            valueC: item.valueC,
            phaseLabels: ['A相', 'B相', 'C相']
        }
    }

    return {
        name,
        otherName,
        unit: item.unit ?? null,
        type: 'scalar',
        valueM: item.valueM
    }
}

const normalizeMetrics = (data: RawMetricItem[]): DisplayMetric[] => {
    if (!data?.length) return []
    if (isNewFormat(data[0])) {
        return groupByName(data).map(groupToMetric)
    }
    return data.map(normalizeOldItem)
}

const toMetricItem = (data: DisplayMetric): MetricItem => ({
    type: data.type,
    data
})

const buildRow = (columns: MetricItem[]) => {
    const row: Record<string, unknown> = {}
    columns.forEach((item, colIndex) => {
        if (item.type === 'abc') {
            row[`v${colIndex}A`] = item.data.valueA
            row[`v${colIndex}B`] = item.data.valueB
            row[`v${colIndex}C`] = item.data.valueC
        } else if (item.type === 't-multi') {
            item.data.subItems?.forEach((sub, subIndex) => {
                row[`t${colIndex}_${subIndex}`] = sub.value
            })
        } else {
            row[`s${colIndex}`] = item.data.valueM
        }
    })
    return row
}

const buildTableSections = (metrics: DisplayMetric[]) => {
    const unified = metrics.map(toMetricItem)
    const sections: TableSection[] = []
    let index = 0

    while (index < unified.length) {
        const remaining = unified.length - index
        let slotsPerRow = isGroupedColumn(unified[index]) ? ROW_WITH_GROUP : ROW_SCALAR_ONLY

        if (slotsPerRow === ROW_SCALAR_ONLY) {
            const peekCount = Math.min(ROW_SCALAR_ONLY, remaining)
            if (unified.slice(index, index + peekCount).some(isGroupedColumn)) {
                slotsPerRow = ROW_WITH_GROUP
            }
        }

        const columns = unified.slice(index, index + Math.min(slotsPerRow, remaining))
        index += columns.length

        sections.push({
            columns,
            row: buildRow(columns),
            slotsPerRow,
            emptySlotCount: Math.max(0, slotsPerRow - columns.length)
        })
    }

    return sections
}

const setData = (data: RawMetricItem[], _targetType?: unknown) => {
    const list = JSON.parse(JSON.stringify(data || [])) as RawMetricItem[]
    tableSections.value = buildTableSections(normalizeMetrics(list))
}

defineExpose({ setData })
</script>

<style lang="scss" scoped>
.near-realtime-data {
    width: 100%;
    height: 100%;
    overflow: hidden;
    box-sizing: border-box;
}

.view_bot {
    height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    box-sizing: border-box;
}

.table-gap {
    height: 20px;
}


</style>
