<template>
    <div class="default-main manage-process" :style="{ height: pageHeight.height }">
        <DeviceTree @node-click="nodeClick" @init="nodeClick" @deviceTypeChange=""></DeviceTree>
        <div class="manage-process-right">
            <div class="process-flow-section">
                <el-descriptions title="数据链路">
                  
                </el-descriptions>
                <div class="process-flow-wrap">
                    <div class="process-flow">
                        <template v-for="(node, index) in flowNodes" :key="node.label">
                            <div class="process-step">
                                <div class="process-node" :class="`process-node--${getNodeState(index)}`">
                                    <div class="process-node__icon" :style="{ color: getNodeColor(index) }">
                                        <el-icon v-if="node.isEl"><component :is="node.icon" /></el-icon>
                                        <component v-else :is="node.icon" :color="getNodeColor(index)" />
                                    </div>
                                    <div class="process-node__name">{{ node.label }}</div>
                                </div>
                            </div>
                            <div
                                v-if="index < flowNodes.length - 1"
                                class="process-bridge"
                                :class="`process-bridge--${getLineState(index)}`"
                            >
                                <svg class="process-bridge__svg" viewBox="0 0 120 16" preserveAspectRatio="none">
                                    <line class="process-bridge__bg" x1="0" y1="8" x2="104" y2="8" />
                                    <line class="process-bridge__active" x1="0" y1="8" x2="104" y2="8" />
                                    <line
                                        v-if="getLineState(index) === 'flowing'"
                                        class="process-bridge__flow"
                                        x1="0"
                                        y1="8"
                                        x2="104"
                                        y2="8"
                                    />
                                    <polygon class="process-bridge__head" points="106,4 116,8 106,12" />
                                </svg>
                            </div>
                        </template>
                    </div>
                </div>
            </div>
            <div class="process-list-section">
                <el-descriptions title="数据列表" />
                <div class="process-list-body">
                    <el-table :data="processTableData" border stripe height="100%">
                        <el-table-column type="index" label="序号" width="60" align="center" />
                        <el-table-column prop="name" label="节点" min-width="100" align="center" />
                        <el-table-column prop="link" label="上游链路" min-width="140" align="center" />
                        <el-table-column prop="status" label="连接状态" min-width="100" align="center">
                            <template #default="{ row }">
                                <span :class="`process-table-status process-table-status--${row.state}`">
                                    {{ row.status }}
                                </span>
                            </template>
                        </el-table-column>
                    </el-table>
                </div>
            </div>
        </div>
    </div>
</template>
<script setup lang="ts">
import { h, watch, onUnmounted, ref, computed, defineComponent } from 'vue'
import { Monitor, Coin } from '@element-plus/icons-vue'
import { mainHeight } from '@/utils/layout'
import DeviceTree from '@/components/tree/govern/deviceTree.vue'

defineOptions({
    name: 'process'
})

const pageHeight = mainHeight(20)

/** 连接状态：null-初始，0-终端绿/终端↔前置，1-前置绿/前置↔MQTT，2-MQTT绿/MQTT↔数据库，3-数据库绿/数据库↔Web，4-全部完成 */
const linkStatus = ref<number | null>(2)

const COLOR = {
    pending: '#94a3b8',
    success: '#009f05',
    error: '#e05252'
}

const createProcessIcon = (render: (color: string) => ReturnType<typeof h>[]) =>
    defineComponent({
        props: {
            color: { type: String, default: COLOR.pending }
        },
        setup(props) {
            return () =>
                h(
                    'svg',
                    {
                        viewBox: '0 0 48 48',
                        fill: 'none',
                        xmlns: 'http://www.w3.org/2000/svg',
                        class: 'process-node__svg'
                    },
                    render(props.color)
                )
        }
    })

/** 终端：采集设备 */
const IconTerminal = createProcessIcon(c => [
    h('rect', { x: 8, y: 10, width: 32, height: 22, rx: 3, stroke: c, 'stroke-width': 2.2 }),
    h('rect', { x: 14, y: 15, width: 20, height: 12, rx: 1.5, stroke: c, 'stroke-width': 1.5, opacity: 0.45 }),
    h('path', { d: 'M4 36h40', stroke: c, 'stroke-width': 2.2, 'stroke-linecap': 'round' }),
    h('path', { d: 'M22 32v4', stroke: c, 'stroke-width': 2.2, 'stroke-linecap': 'round' }),
    h('circle', { cx: 38, cy: 8, r: 3, fill: c, opacity: 0.85 })
])

/** 前置：双机服务器 */
const IconFrontEnd = createProcessIcon(c => [
    h('rect', { x: 10, y: 8, width: 28, height: 13, rx: 2.5, stroke: c, 'stroke-width': 2.2, fill: 'rgba(148,163,184,0.08)' }),
    h('circle', { cx: 16, cy: 14.5, r: 2, fill: c }),
    h('line', { x1: 22, y1: 14.5, x2: 34, y2: 14.5, stroke: c, 'stroke-width': 1.5, opacity: 0.5 }),
    h('rect', { x: 10, y: 27, width: 28, height: 13, rx: 2.5, stroke: c, 'stroke-width': 2.2, fill: 'rgba(148,163,184,0.08)' }),
    h('circle', { cx: 16, cy: 33.5, r: 2, fill: c }),
    h('line', { x1: 22, y1: 33.5, x2: 34, y2: 33.5, stroke: c, 'stroke-width': 1.5, opacity: 0.5 })
])

/** MQTT：云 + 节点 */
const IconMqtt = createProcessIcon(c => [
    h('circle', { cx: 24, cy: 26, r: 2.5, fill: c }),
    h('circle', { cx: 12, cy: 16, r: 2, fill: c, opacity: 0.75 }),
    h('circle', { cx: 36, cy: 16, r: 2, fill: c, opacity: 0.75 }),
    h('circle', { cx: 12, cy: 36, r: 2, fill: c, opacity: 0.75 }),
    h('circle', { cx: 36, cy: 36, r: 2, fill: c, opacity: 0.75 }),
    h('line', { x1: 24, y1: 26, x2: 12, y2: 16, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
    h('line', { x1: 24, y1: 26, x2: 36, y2: 16, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
    h('line', { x1: 24, y1: 26, x2: 12, y2: 36, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
    h('line', { x1: 24, y1: 26, x2: 36, y2: 36, stroke: c, 'stroke-width': 1.4, opacity: 0.55 }),
    h('path', {
        d: 'M15 24c0-4.5 3.5-7.5 9-7.5s9 3 9 7.5c3.5 0 6 2.5 6 6s-2.5 6-6 6H15c-3.5 0-6-2.5-6-6s2.5-6 6-6z',
        stroke: c,
        'stroke-width': 2,
        fill: '#fff'
    }),
    h('text', { x: 24, y: 27.5, 'text-anchor': 'middle', fill: c, 'font-size': 7.5, 'font-weight': 'bold' }, 'MQTT')
])

const flowNodes = [
    { label: '终端', icon: IconTerminal },
    { label: '前置', icon: IconFrontEnd },
    { label: 'MQTT', icon: IconMqtt },
    { label: '数据库', icon: Coin, isEl: true },
    { label: 'Web', icon: Monitor, isEl: true }
]

/** 全部完成状态值 */
const COMPLETE_STATUS = 4
/** 连线数量 */
const LINE_COUNT = flowNodes.length - 1

const createErrorArray = <T,>(length: number, value: T) => Array.from({ length }, () => value)

type NodeState = 'pending' | 'success' | 'error'
type LineState = 'idle' | 'flowing' | 'success' | 'error'

const lineErrors = ref(createErrorArray(LINE_COUNT, false))
const nodeErrors = ref(createErrorArray(flowNodes.length, false))
let timeoutTimer: ReturnType<typeof setTimeout> | null = null

const clearTimeoutTimer = () => {
    if (timeoutTimer) {
        clearTimeout(timeoutTimer)
        timeoutTimer = null
    }
}

const resetErrorsFrom = (fromIndex: number) => {
    for (let i = fromIndex; i < lineErrors.value.length; i++) {
        lineErrors.value[i] = false
    }
    for (let i = fromIndex + 1; i < nodeErrors.value.length; i++) {
        nodeErrors.value[i] = false
    }
}

const resetAllErrors = () => {
    lineErrors.value = createErrorArray(LINE_COUNT, false)
    nodeErrors.value = createErrorArray(flowNodes.length, false)
}

const startTimeout = (status: number | null) => {
    clearTimeoutTimer()
    if (status === null || status >= COMPLETE_STATUS) return

    const capturedStatus = status
    timeoutTimer = setTimeout(() => {
        if (linkStatus.value !== capturedStatus) return
        lineErrors.value[capturedStatus] = true
        nodeErrors.value[capturedStatus + 1] = true
    }, 5000)
}

watch(
    linkStatus,
    (val, oldVal) => {
        const prev = oldVal ?? null
        if (val !== null && (prev === null || val > prev)) {
            resetErrorsFrom(val)
        } else if (val !== null && prev !== null && val < prev) {
            resetAllErrors()
        } else if (val === null) {
            resetAllErrors()
        }
        startTimeout(val)
    },
    { immediate: true }
)

onUnmounted(clearTimeoutTimer)

const getNodeState = (index: number): NodeState => {
    if (nodeErrors.value[index]) return 'error'
    if (linkStatus.value === null) return 'pending'
    if (linkStatus.value >= COMPLETE_STATUS) return 'success'
    if (index <= linkStatus.value) return 'success'
    return 'pending'
}

const getLineState = (index: number): LineState => {
    if (lineErrors.value[index]) return 'error'
    if (linkStatus.value === null) return 'idle'
    if (linkStatus.value >= COMPLETE_STATUS) return 'success'
    if (index < linkStatus.value) return 'success'
    if (index === linkStatus.value) return 'flowing'
    return 'idle'
}

const getNodeColor = (index: number) => COLOR[getNodeState(index)]

const getNodeStatusText = (index: number) => {
    const state = getNodeState(index)
    if (state === 'error') return '连接失败'
    if (state === 'success') return '连接成功'
    if (linkStatus.value !== null && linkStatus.value < COMPLETE_STATUS && index === linkStatus.value + 1) {
        return '连接中'
    }
    return '待连接'
}







const processTableData = computed(() =>
    flowNodes.map((node, index) => ({
        name: node.label,
        link: index === 0 ? '-' : `${flowNodes[index - 1].label} → ${node.label}`,
        status: getNodeStatusText(index),
        state: getNodeState(index)
    }))
)

const nodeClick = async (e: anyObj) => {
    console.log('点击设备树节点')
}
</script>
<style scoped lang="scss">
$green: #009f05;
$green-light: #e8f7e9;
$red: #e05252;
$red-light: #fdeeee;
$gray: #cbd5e1;
$gray-text: #94a3b8;
$node-min: 110px;
$node-max: 120px;
$bridge-min: 60px;

.manage-process {
    display: flex;
    height: 100%;

    &-right {
        display: flex;
        flex-direction: column;
        flex: 1;
        height: 100%;
        min-height: 0;
        overflow: hidden;
        padding: 10px 10px 10px 0;

        :deep(.el-descriptions__header) {
            height: 36px;
            margin-bottom: 7px;
            display: flex;
            align-items: center;
            flex-shrink: 0;
        }
    }
}

.process-flow-section {
    flex-shrink: 0;
}

.process-list-section {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    margin-top: 8px;
}

.process-list-body {
    flex: 1;
    min-height: 0;
}

.process-table-status {
    &--success {
        color: $green;
    }

    &--error {
        color: $red;
    }

    &--pending {
        color: $gray-text;
    }
}

.process-flow-wrap {
    width: 100%;
    display: flex;
    align-items: flex-start;
    padding-bottom: 8px;
    padding: 0 10%;
    overflow-x: auto;
}

.process-flow {
    display: flex;
    align-items: center;
    width: 100%;
    min-width: min(100%, calc(#{$node-min} * 5 + #{$bridge-min} * 4));
    min-height: $node-min;
   
    box-sizing: border-box;
}

.process-step {
    flex: 1 1 $node-min;
    min-width: $node-min;
    max-width: $node-max;
    display: flex;
    justify-content: center;
}

.process-node {
    position: relative;
    width: 100%;
    min-width: $node-min;
    min-height: $node-min;
    max-width: $node-max;
    aspect-ratio: 1;
    height: auto;
    padding: clamp(8px, 8%, 12px);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    border-radius: 16px;
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);

    &__index {
        position: absolute;
        top: 8px;
        left: 10px;
        font-size: 11px;
        font-weight: 700;
        color: #cbd5e1;
        font-variant-numeric: tabular-nums;
        transition: color 0.3s;
    }

    &__icon {
        display: flex;
        align-items: center;
        justify-content: center;
        width: clamp(52px, 48%, 60px);
        height: clamp(52px, 48%, 60px);
        border-radius: 12px;
        background: #fff;
        box-shadow: 0 2px 8px rgba(15, 23, 42, 0.06);
        transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);

        .el-icon {
            font-size: clamp(34px, 32%, 40px);
        }

        .process-node__svg {
            width: clamp(34px, 32%, 40px);
            height: clamp(34px, 32%, 40px);
            display: block;
        }
    }

    &__name {
        margin-top: 8px;
        font-size: clamp(13px, 12%, 15px);
        font-weight: 600;
        color: #334155;
        letter-spacing: 1px;
        line-height: 1.2;
    }

    &__status {
        margin-top: 4px;
        font-size: clamp(12px, 11%, 13px);
        color: $gray-text;
        transition: color 0.3s;
        line-height: 1.2;
    }

    &--success {
        background: linear-gradient(160deg, #fff 0%, $green-light 100%);
        border-color: rgba(0, 159, 5, 0.25);
        box-shadow: 0 4px 16px rgba(0, 159, 5, 0.1);

        .process-node__index {
            color: rgba(0, 159, 5, 0.45);
        }

        .process-node__icon {
            background: #fff;
            box-shadow: 0 0 0 2px rgba(0, 159, 5, 0.15);
        }

        .process-node__status {
            color: $green;
        }
    }

    &--error {
        background: linear-gradient(160deg, #fff 0%, $red-light 100%);
        border-color: rgba(224, 82, 82, 0.3);
        box-shadow: 0 4px 16px rgba(224, 82, 82, 0.1);

        .process-node__index {
            color: rgba(224, 82, 82, 0.45);
        }

        .process-node__icon {
            box-shadow: 0 0 0 2px rgba(224, 82, 82, 0.15);
        }

        .process-node__status {
            color: $red;
        }
    }
}

.process-bridge {
    flex: 1 1 0;
    min-width: $bridge-min;
    height: 25px;
    padding: 0 2px;
    align-self: center;

    &__svg {
        width: 100%;
        height: 100%;
        overflow: visible;
        display: block;
    }

    &__bg {
        stroke: #e8edf3;
        stroke-width: 5;
        stroke-linecap: round;
        transition: stroke 0.4s;
    }

    &__active {
        stroke: transparent;
        stroke-width: 3;
        stroke-linecap: round;
        transition: stroke 0.4s;
    }

    &__flow {
        stroke: $green;
        stroke-width: 3;
        stroke-linecap: round;
        stroke-dasharray: 5 9;
        animation: bridge-dash 0.85s linear infinite;
    }

    &__head {
        fill: #cbd5e1;
        transition: fill 0.4s;
    }

    &--success {
        .process-bridge__active {
            stroke: $green;
            filter: drop-shadow(0 0 3px rgba(0, 159, 5, 0.35));
        }

        .process-bridge__head {
            fill: $green;
        }
    }

    &--error {
        .process-bridge__active {
            stroke: $red;
            filter: drop-shadow(0 0 3px rgba(224, 82, 82, 0.35));
        }

        .process-bridge__head {
            fill: $red;
        }
    }

    &--flowing {
        .process-bridge__active {
            stroke: #dce3ec;
            stroke-width: 2;
        }

        .process-bridge__head {
            fill: $green;
            animation: head-pulse 1.2s ease-in-out infinite;
        }
    }

    &--idle {
        .process-bridge__active {
            stroke: transparent;
        }

        .process-bridge__head {
            fill: #cbd5e1;
        }
    }
}

@keyframes bridge-dash {
    to {
        stroke-dashoffset: -14;
    }
}

@keyframes head-pulse {
    0%,
    100% {
        opacity: 0.45;
        transform: translateX(0);
    }

    50% {
        opacity: 1;
    }
}
</style>
