<template>
    <section class="card trend-chart-panel">
        <div class="panel-header">
            <SteadyTrendChartTools
                :tool-groups="trendToolGroups"
                :is-tool-active="isTrendToolActive"
                :is-tool-disabled="isTrendToolDisabled"
                :get-tool-tooltip="getTrendToolTooltip"
                @tool-action="handleTrendToolAction"
            />
            <span v-if="trendResult" class="panel-meta">总点数：{{ trendResult.displayPointCount || 0 }}</span>
        </div>

        <div class="chart-panel-body" v-loading="loading">
            <div
                v-if="hasChartFrame"
                ref="chartExportTargetRef"
                class="chart-list steady-trend-export-target"
                :style="{ '--steady-trend-visible-chart-count': normalVisibleChartCount }"
            >
                <div v-for="group in chartGroups" :key="group.key" class="chart-group">
                    <div class="chart-body">
                        <LineChart
                            :options="group.options"
                            :group="group.group"
                            @chart-data-zoom="handleChartDataZoom"
                        />
                    </div>
                </div>
            </div>
            <el-empty v-else-if="hasQueriedWithoutData" class="chart-empty" description="暂无数据" />
            <el-empty v-else class="chart-empty" description="请选择监测点和指标后查询趋势" />
        </div>

        <SteadyTrendFullscreen
            v-model="fullscreenVisible"
            :chart-groups="chartGroups"
            :visible-chart-count="fullscreenVisibleChartCount"
            :tool-groups="fullscreenToolGroups"
            :is-tool-active="isTrendToolActive"
            :is-tool-disabled="isTrendToolDisabled"
            :get-tool-tooltip="getTrendToolTooltip"
            @chart-data-zoom="handleChartDataZoom"
            @tool-action="handleTrendToolAction"
        />

        <SteadyTrendDataTableDialog v-model="dataTableVisible" :trend-result="trendResult" />
    </section>
</template>

<script setup lang="ts">
import {
    ArrowDownBold,
    ArrowLeftBold,
    ArrowRightBold,
    ArrowUpBold,
    Crop,
    DataAnalysis,
    DataLine,
    FullScreen,
    Mouse,
    Picture,
    Pointer,
    RefreshLeft
} from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import html2canvas from 'html2canvas'
import { computed, nextTick, ref, watch } from 'vue'
import LineChart from '@/components/echarts/line/index.vue'
import type { SteadyDataView } from '@/api/steady/steadyDataView/interface'
import { buildSteadyTrendChartGroups, type SteadyTrendZoomRange } from '../utils/trendOptions'
import type { SteadyTrendToolAction, SteadyTrendToolGroup, SteadyTrendToolItem } from './chartTools'
import SteadyTrendChartTools from './SteadyTrendChartTools.vue'
import SteadyTrendDataTableDialog from './SteadyTrendDataTableDialog.vue'
import SteadyTrendFullscreen from './SteadyTrendFullscreen.vue'

defineOptions({
    name: 'SteadyTrendChartPanel'
})

const props = defineProps<{
    trendResult: SteadyDataView.SteadyTrendQueryResult | null
    loading: boolean
}>()

type SteadyTrendInteractionMode = 'none' | 'box-zoom' | 'pan'
const trendToolGroups: SteadyTrendToolGroup[] = [
    {
        key: 'viewport',
        items: [
            { action: 'x-zoom-in', label: 'X坐标放大', icon: ArrowRightBold },
            { action: 'x-zoom-out', label: 'X坐标缩小', icon: ArrowLeftBold },
            { action: 'y-zoom-in', label: 'Y坐标放大', icon: ArrowUpBold },
            { action: 'y-zoom-out', label: 'Y坐标缩小', icon: ArrowDownBold },
            { action: 'box-zoom', label: '框选放大', icon: Crop },
            { action: 'reset', label: '恢复', icon: RefreshLeft },
            { action: 'pan', label: '平移', icon: Pointer },
            { action: 'wheel-zoom', label: '滚轮缩放', icon: Mouse }
        ]
    },
    {
        key: 'view',
        items: [
            { action: 'missing-data', label: '缺失数据', icon: DataLine },
            { action: 'fullscreen', label: '全屏展示', icon: FullScreen },
            { action: 'download-image', label: '下载图片', icon: Picture },
            { action: 'query-data', label: '数据查询', icon: DataAnalysis }
        ]
    }
]

const clampPercent = (value: number) => Math.min(Math.max(value, 0), 100)
const DEFAULT_STEADY_TREND_X_ZOOM_RANGE: SteadyTrendZoomRange = { start: 0, end: 100 }
const STEADY_TREND_DAY_MS = 24 * 60 * 60 * 1000
const STEADY_TREND_HALF_RANGE_DAYS = 20
const STEADY_TREND_QUARTER_RANGE_DAYS = 30
const STEADY_TREND_TENTH_RANGE_DAYS = 60
const trendXZoomRange = ref<SteadyTrendZoomRange>({ ...DEFAULT_STEADY_TREND_X_ZOOM_RANGE })
const defaultTrendXZoomRange = ref<SteadyTrendZoomRange>({ ...DEFAULT_STEADY_TREND_X_ZOOM_RANGE })
const trendYZoomScale = ref(1)
const activeTrendInteractionMode = ref<SteadyTrendInteractionMode>('none')
const wheelZoomEnabled = ref(false)
const missingDataEnabled = ref(true)
const fullscreenVisible = ref(false)
const dataTableVisible = ref(false)
const chartExportTargetRef = ref<HTMLElement>()
const hasSeries = computed(() => Boolean(props.trendResult?.series?.length))
const hasQueryTimeRange = computed(() => Boolean(props.trendResult?.queryTimeStart && props.trendResult?.queryTimeEnd))
const hasDataPoints = computed(() =>
    Boolean(
        props.trendResult?.series?.some(series =>
            (series.points || []).some(point => typeof point.value === 'number' && Number.isFinite(point.value))
        )
    )
)
const hasChartFrame = computed(() => hasSeries.value || (hasQueryTimeRange.value && !props.trendResult?.queryCompleted))
const hasQueriedWithoutData = computed(() => Boolean(props.trendResult?.queryCompleted && !hasDataPoints.value))
const chartGroups = computed(() =>
    buildSteadyTrendChartGroups(props.trendResult?.series || [], trendXZoomRange.value, {
        activeTool: activeTrendInteractionMode.value,
        wheelZoomEnabled: wheelZoomEnabled.value,
        showMissingData: missingDataEnabled.value,
        yZoomScale: trendYZoomScale.value,
        queryTimeStart: props.trendResult?.queryTimeStart,
        queryTimeEnd: props.trendResult?.queryTimeEnd
    })
)
const normalVisibleChartCount = computed(() => Math.max(Math.min(chartGroups.value.length, 3), 1))
const fullscreenVisibleChartCount = computed(() => Math.max(Math.min(chartGroups.value.length, 6), 1))
const fullscreenToolGroups = computed(() =>
    trendToolGroups
        .map(group => ({
            ...group,
            items: group.items.filter(item => item.action !== 'fullscreen')
        }))
        .filter(group => group.items.length)
)
const canPanTrendChart = computed(() => {
    const { start, end } = trendXZoomRange.value

    return hasSeries.value && (start > 0 || end < 100)
})
const isDefaultTrendXZoomRange = computed(() => {
    const { start, end } = trendXZoomRange.value
    const defaultRange = defaultTrendXZoomRange.value

    return start === defaultRange.start && end === defaultRange.end
})
const canResetTrendChart = computed(() => {
    const changedYZoom = trendYZoomScale.value !== 1
    const changedInteractionMode = activeTrendInteractionMode.value !== 'none'
    const changedWheelZoom = wheelZoomEnabled.value

    return (
        hasSeries.value &&
        (!isDefaultTrendXZoomRange.value || changedYZoom || changedInteractionMode || changedWheelZoom)
    )
})

const parseSteadyTrendTime = (value?: string) => {
    if (!value) return null

    const timestamp = Date.parse(value.replace(' ', 'T'))

    return Number.isFinite(timestamp) ? timestamp : null
}

const resolveSteadyTrendTimeRangeMs = (trendResult: SteadyDataView.SteadyTrendQueryResult | null) => {
    let minTime = Number.POSITIVE_INFINITY
    let maxTime = Number.NEGATIVE_INFINITY

    trendResult?.series?.forEach(series => {
        series.points?.forEach(point => {
            const timestamp = parseSteadyTrendTime(point.time)

            if (timestamp === null) return

            minTime = Math.min(minTime, timestamp)
            maxTime = Math.max(maxTime, timestamp)
        })
    })

    return Number.isFinite(minTime) && Number.isFinite(maxTime) && maxTime > minTime ? maxTime - minTime : 0
}

const resolveSteadyTrendDefaultZoomRange = (trendResult: SteadyDataView.SteadyTrendQueryResult | null) => {
    const timeRangeMs = resolveSteadyTrendTimeRangeMs(trendResult)
    const timeRangeDays = timeRangeMs / STEADY_TREND_DAY_MS

    if (timeRangeDays > STEADY_TREND_TENTH_RANGE_DAYS) return { start: 0, end: 10 }
    if (timeRangeDays > STEADY_TREND_QUARTER_RANGE_DAYS) return { start: 0, end: 25 }
    if (timeRangeDays > STEADY_TREND_HALF_RANGE_DAYS) return { start: 0, end: 50 }

    return { ...DEFAULT_STEADY_TREND_X_ZOOM_RANGE }
}

const resetTrendToolState = () => {
    trendXZoomRange.value = { ...defaultTrendXZoomRange.value }
    trendYZoomScale.value = 1
    activeTrendInteractionMode.value = 'none'
    wheelZoomEnabled.value = false
}

const zoomTrendXAxis = (ratio: number) => {
    const { start, end } = trendXZoomRange.value
    const center = (start + end) / 2
    const nextWidth = Math.min(Math.max((end - start) * ratio, 1), 100)
    const nextStart = clampPercent(center - nextWidth / 2)
    const nextEnd = clampPercent(center + nextWidth / 2)

    if (nextStart === 0) {
        trendXZoomRange.value = { start: 0, end: nextWidth }
        return
    }

    if (nextEnd === 100) {
        trendXZoomRange.value = { start: 100 - nextWidth, end: 100 }
        return
    }

    trendXZoomRange.value = { start: nextStart, end: nextEnd }
}

const isTrendToolActive = (action: SteadyTrendToolAction) => {
    if (action === 'fullscreen') return fullscreenVisible.value
    if (action === 'wheel-zoom') return wheelZoomEnabled.value
    if (action === 'missing-data') return missingDataEnabled.value
    if (action === 'box-zoom' || action === 'pan') return activeTrendInteractionMode.value === action

    return false
}

const isTrendToolDisabled = (action: SteadyTrendToolAction) => {
    if (action === 'query-data') return false
    if (!hasChartFrame.value) return true
    if (action === 'pan') return !canPanTrendChart.value
    if (action === 'reset') return !canResetTrendChart.value

    return false
}

const getTrendToolTooltip = (item: SteadyTrendToolItem) => {
    if (item.action === 'pan' && isTrendToolDisabled(item.action) && hasSeries.value) {
        return '请先放大 X 轴或框选局部区域后再平移'
    }

    return item.label
}

const downloadSteadyTrendImage = async () => {
    await nextTick()

    const targetElement = fullscreenVisible.value
        ? (document.querySelector('.steady-trend-fullscreen__chart-list') as HTMLElement | null)
        : chartExportTargetRef.value

    if (!targetElement) {
        ElMessage.warning('暂无可下载的趋势图')
        return
    }

    const canvas = await html2canvas(targetElement, {
        backgroundColor: '#ffffff',
        scale: window.devicePixelRatio || 1,
        useCORS: true
    })
    const imageUrl = canvas.toDataURL('image/png')
    const exportFile = document.createElement('a')

    exportFile.style.display = 'none'
    exportFile.download = `steady-trend-${Date.now()}.png`
    exportFile.href = imageUrl
    document.body.appendChild(exportFile)
    exportFile.click()
    document.body.removeChild(exportFile)

    ElMessage.success('趋势图图片下载成功')
}

const handleTrendToolAction = async (action: SteadyTrendToolAction) => {
    if (isTrendToolDisabled(action)) return

    switch (action) {
        case 'x-zoom-in':
            zoomTrendXAxis(0.8)
            break
        case 'x-zoom-out':
            zoomTrendXAxis(1.25)
            break
        case 'y-zoom-in':
            trendYZoomScale.value = Math.max(trendYZoomScale.value * 0.8, 0.1)
            break
        case 'y-zoom-out':
            trendYZoomScale.value = Math.min(trendYZoomScale.value * 1.25, 10)
            break
        case 'box-zoom':
            activeTrendInteractionMode.value = activeTrendInteractionMode.value === 'box-zoom' ? 'none' : 'box-zoom'
            break
        case 'wheel-zoom':
            wheelZoomEnabled.value = !wheelZoomEnabled.value
            break
        case 'missing-data':
            missingDataEnabled.value = !missingDataEnabled.value
            break
        case 'pan':
            if (!canPanTrendChart.value) {
                ElMessage.info('请先放大 X 轴或框选局部区域后再平移')
                activeTrendInteractionMode.value = 'none'
                break
            }
            activeTrendInteractionMode.value = activeTrendInteractionMode.value === 'pan' ? 'none' : 'pan'
            break
        case 'reset':
            resetTrendToolState()
            break
        case 'fullscreen':
            fullscreenVisible.value = true
            break
        case 'download-image':
            await downloadSteadyTrendImage()
            break
        case 'query-data':
            if (!hasSeries.value) {
                ElMessage.warning('请先查询趋势数据')
                break
            }
            dataTableVisible.value = true
            break
        default:
            break
    }
}

const handleChartDataZoom = (value: SteadyTrendZoomRange) => {
    trendXZoomRange.value = {
        start: clampPercent(value.start),
        end: clampPercent(value.end)
    }

    if (!canPanTrendChart.value && activeTrendInteractionMode.value === 'pan') {
        activeTrendInteractionMode.value = 'none'
    }
}

watch(
    () => props.trendResult,
    () => {
        // 新查询结果按当前数据量重置默认窗口，避免沿用上一批数据的局部缩放范围。
        defaultTrendXZoomRange.value = resolveSteadyTrendDefaultZoomRange(props.trendResult)
        resetTrendToolState()
    }
)
</script>

<style scoped lang="scss">
.trend-chart-panel {
    --steady-trend-chart-gap: 8px;
    --steady-trend-visible-chart-count: 3;

    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
    overflow: hidden;
    padding: 12px;
}

.panel-header {
    display: flex;
    flex: none;
    align-items: center;
    justify-content: flex-start;
    gap: 0;
    margin-bottom: 10px;
}

.panel-meta {
    margin-left: 15px;
    color: var(--el-text-color-secondary);
    font-size: 12px;
    white-space: nowrap;
}

.chart-panel-body {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    min-height: 0;
}

.chart-list {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--steady-trend-chart-gap);
    min-height: 0;
    overflow-x: hidden;
    overflow-y: auto;
}

.chart-group {
    box-sizing: border-box;
    display: flex;
    flex: 0 0
        calc(
            (100% - var(--steady-trend-chart-gap) * (var(--steady-trend-visible-chart-count) - 1)) /
                var(--steady-trend-visible-chart-count)
        );
    flex-direction: column;
    min-height: 0;
    overflow: hidden;
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 4px;
    background: var(--cn-color-canvas-bg);
}

.chart-body {
    flex: 1;
    min-height: 0;
    padding: 0 8px 8px;
}

.chart-empty {
    flex: 1;
}
</style>
