import type { RecordIotenvInstantVO } from '@/api/ems/types';
import * as echarts from 'echarts';

export interface CurveChartExpose {
  chart?: unknown;
  setData?: (option: echarts.EChartsOption) => void;
}

export interface IotCurveMetricHint {
  key?: string;
  label?: string;
  unit?: string;
  aliases?: string[];
}

export interface IotCurveMetricDefinition {
  key: string;
  label: string;
  unit: string;
  aliases: string[];
  color: string;
  areaStartColor: string;
  areaEndColor: string;
}

export interface NormalizedIotCurveRecord extends RecordIotenvInstantVO {
  __timeKey: string;
  __sortValue: number;
}

const BASE_METRIC_CATALOG: IotCurveMetricDefinition[] = [
  {
    key: 'temperature',
    label: '温度',
    unit: '℃',
    aliases: ['temperature', 'tempreture'],
    color: '#ff7a45',
    areaStartColor: 'rgba(255, 122, 69, 0.28)',
    areaEndColor: 'rgba(255, 122, 69, 0.02)'
  },
  {
    key: 'humidity',
    label: '湿度',
    unit: '%',
    aliases: ['humidity'],
    color: '#14b8a6',
    areaStartColor: 'rgba(20, 184, 166, 0.26)',
    areaEndColor: 'rgba(20, 184, 166, 0.02)'
  },
  {
    key: 'noise',
    label: '噪声',
    unit: 'dB',
    aliases: ['noise'],
    color: '#8b5cf6',
    areaStartColor: 'rgba(139, 92, 246, 0.24)',
    areaEndColor: 'rgba(139, 92, 246, 0.02)'
  },
  {
    key: 'illuminance',
    label: '照度',
    unit: 'Lux',
    aliases: ['illuminance'],
    color: '#f59e0b',
    areaStartColor: 'rgba(245, 158, 11, 0.24)',
    areaEndColor: 'rgba(245, 158, 11, 0.02)'
  },
  {
    key: 'concentration',
    label: '浓度',
    unit: 'ppm',
    aliases: ['concentration'],
    color: '#ef4444',
    areaStartColor: 'rgba(239, 68, 68, 0.24)',
    areaEndColor: 'rgba(239, 68, 68, 0.02)'
  },
  {
    key: 'vibrationSpeed',
    label: '振动速度',
    unit: 'mm/s',
    aliases: ['vibrationSpeed', 'speed'],
    color: '#2563eb',
    areaStartColor: 'rgba(37, 99, 235, 0.24)',
    areaEndColor: 'rgba(37, 99, 235, 0.02)'
  },
  {
    key: 'vibrationDisplacement',
    label: '振动位移',
    unit: 'um',
    aliases: ['vibrationDisplacement', 'displacement'],
    color: '#0f766e',
    areaStartColor: 'rgba(15, 118, 110, 0.24)',
    areaEndColor: 'rgba(15, 118, 110, 0.02)'
  },
  {
    key: 'vibrationAcceleration',
    label: '振动加速度',
    unit: 'g',
    aliases: ['vibrationAcceleration', 'acceleration'],
    color: '#db2777',
    areaStartColor: 'rgba(219, 39, 119, 0.24)',
    areaEndColor: 'rgba(219, 39, 119, 0.02)'
  },
  {
    key: 'vibrationTemp',
    label: '振动温度',
    unit: '℃',
    aliases: ['vibrationTemp'],
    color: '#f97316',
    areaStartColor: 'rgba(249, 115, 22, 0.24)',
    areaEndColor: 'rgba(249, 115, 22, 0.02)'
  }
];

const toNumberOrNull = (value: unknown) => {
  if (value === null || value === undefined || value === '') {
    return null;
  }
  const nextValue = Number(value);
  return Number.isFinite(nextValue) ? nextValue : null;
};

const uniqueStrings = (values: Array<string | undefined | null>) => {
  return Array.from(
    new Set(
      values
        .map((item) => String(item || '').trim())
        .filter((item) => item.length > 0)
    )
  );
};

const createFallbackMetricDefinition = (hint: IotCurveMetricHint, index: number): IotCurveMetricDefinition => {
  const colorPalette = [
    ['#2563eb', 'rgba(37, 99, 235, 0.24)', 'rgba(37, 99, 235, 0.02)'],
    ['#14b8a6', 'rgba(20, 184, 166, 0.24)', 'rgba(20, 184, 166, 0.02)'],
    ['#f97316', 'rgba(249, 115, 22, 0.24)', 'rgba(249, 115, 22, 0.02)'],
    ['#8b5cf6', 'rgba(139, 92, 246, 0.24)', 'rgba(139, 92, 246, 0.02)'],
    ['#ef4444', 'rgba(239, 68, 68, 0.24)', 'rgba(239, 68, 68, 0.02)']
  ];
  const [color, areaStartColor, areaEndColor] = colorPalette[index % colorPalette.length];
  const fallbackKey = String(hint.key || hint.aliases?.[0] || `metric_${index + 1}`);

  return {
    key: fallbackKey,
    label: String(hint.label || fallbackKey),
    unit: String(hint.unit || ''),
    aliases: uniqueStrings([fallbackKey, ...(hint.aliases || [])]),
    color,
    areaStartColor,
    areaEndColor
  };
};

const mergeMetricCatalog = (metricHints: IotCurveMetricHint[] = []) => {
  const catalogMap = new Map<string, IotCurveMetricDefinition>();

  BASE_METRIC_CATALOG.forEach((item) => {
    catalogMap.set(item.key, item);
  });

  metricHints.forEach((hint, index) => {
    const key = String(hint.key || hint.aliases?.[0] || '').trim();
    if (!key) {
      return;
    }
    const previous = catalogMap.get(key);
    if (previous) {
      catalogMap.set(key, {
        ...previous,
        label: hint.label || previous.label,
        unit: hint.unit || previous.unit,
        aliases: uniqueStrings([key, ...(previous.aliases || []), ...(hint.aliases || [])])
      });
      return;
    }
    catalogMap.set(key, createFallbackMetricDefinition(hint, index));
  });

  return Array.from(catalogMap.values());
};

const resolveTimeKey = (record: RecordIotenvInstantVO) => {
  return String(record?.recodeTime || record?.collectTime || '');
};

const resolveSortValue = (timeKey: string) => {
  const timestamp = new Date(timeKey).getTime();
  return Number.isFinite(timestamp) ? timestamp : 0;
};

export const normalizeIotCurveRecord = (record: RecordIotenvInstantVO): NormalizedIotCurveRecord => {
  const timeKey = resolveTimeKey(record);
  return {
    ...record,
    __timeKey: timeKey,
    __sortValue: resolveSortValue(timeKey)
  };
};

export const readMetricValue = (record: Record<string, unknown>, metric: IotCurveMetricDefinition) => {
  for (const alias of metric.aliases) {
    const metricValue = toNumberOrNull(record[alias]);
    if (metricValue !== null) {
      return metricValue;
    }
  }
  return null;
};

export const buildMetricHintsByNode = (node?: Record<string, unknown> | null) => {
  if (!node) {
    return [] as IotCurveMetricHint[];
  }

  const metricCode = String(node.metricCode || '').trim();
  const metricName = String(node.metricName || node.pointType || '').trim();
  const unitName = String(node.unitName || '').trim();
  if (!metricCode) {
    return [];
  }

  // 这里优先把树节点元数据也转成候选指标，后续即使后端新增类型，只要字段名与 metricCode 对上就能直接出图。
  return [
    {
      key: metricCode,
      label: metricName || String(node.label || metricCode),
      unit: unitName,
      aliases: [metricCode]
    }
  ];
};

export const resolveIotCurveMetrics = (
  records: Array<Record<string, unknown>>,
  metricHints: IotCurveMetricHint[] = []
) => {
  const catalog = mergeMetricCatalog(metricHints);
  const preferredKeys = uniqueStrings(metricHints.map((item) => item.key));
  const detectedMetrics = catalog.filter((item) => records.some((record) => readMetricValue(record, item) !== null));

  if (!records.length) {
    return catalog.filter((item) => preferredKeys.includes(item.key));
  }

  const resultMap = new Map<string, IotCurveMetricDefinition>();
  preferredKeys.forEach((key) => {
    const metric = catalog.find((item) => item.key === key);
    if (metric && detectedMetrics.some((item) => item.key === key)) {
      resultMap.set(key, metric);
    }
  });
  detectedMetrics.forEach((item) => {
    resultMap.set(item.key, item);
  });

  return Array.from(resultMap.values());
};

export const extractRealtimeIotRecords = (payload: unknown): RecordIotenvInstantVO[] => {
  if (!payload) {
    return [];
  }
  if (Array.isArray(payload)) {
    return payload as RecordIotenvInstantVO[];
  }
  if (typeof payload !== 'object') {
    return [];
  }

  const source = payload as Record<string, unknown>;
  const candidateKeys = ['data', 'rows', 'records', 'list', 'payload'];
  for (const key of candidateKeys) {
    if (Array.isArray(source[key])) {
      return source[key] as RecordIotenvInstantVO[];
    }
  }
  if (resolveTimeKey(source as RecordIotenvInstantVO)) {
    return [source as RecordIotenvInstantVO];
  }
  return [];
};

export const mergeRealtimeWindow = (
  currentRecords: NormalizedIotCurveRecord[],
  incomingRecords: RecordIotenvInstantVO[],
  windowSize: number
) => {
  const mergedMap = new Map<string, NormalizedIotCurveRecord>();
  [...currentRecords, ...incomingRecords.map(normalizeIotCurveRecord)].forEach((item) => {
    if (!item.__timeKey) {
      return;
    }
    const uniqueKey = `${item.monitorId || item.monitorCode || ''}_${item.__timeKey}`;
    const previous = mergedMap.get(uniqueKey);
    mergedMap.set(uniqueKey, {
      ...previous,
      ...item
    });
  });

  const normalizedWindowSize = Math.max(1, Number(windowSize) || 1);
  return Array.from(mergedMap.values())
    .sort((left, right) => {
      if (left.__sortValue !== right.__sortValue) {
        return left.__sortValue - right.__sortValue;
      }
      return left.__timeKey.localeCompare(right.__timeKey);
    })
    .slice(-normalizedWindowSize);
};

export const buildIotCurveOption = ({
  metric,
  monitorName,
  records,
  premium = false,
  subtitle = ''
}: {
  metric: IotCurveMetricDefinition;
  monitorName: string;
  records: Array<Record<string, unknown>>;
  premium?: boolean;
  subtitle?: string;
}): echarts.EChartsOption => {
  const titleText = `${monitorName || '设备'} ${metric.label}曲线`;
  const xData = records.map((item) => String(item.__timeKey || item.recodeTime || item.collectTime || ''));
  const seriesData = records.map((item) => readMetricValue(item, metric));

  return {
    backgroundColor: 'transparent',
    animation: premium,
    animationDuration: premium ? 420 : 0,
    animationDurationUpdate: premium ? 320 : 0,
    title: {
      text: titleText,
      subtext: subtitle,
      left: 'center',
      top: 10,
      textStyle: {
        color: premium ? '#0f172a' : '#0f172a',
        fontSize: premium ? 18 : 16,
        fontWeight: 600
      },
      subtextStyle: {
        color: premium ? '#64748b' : '#64748b',
        fontSize: 12
      }
    },
    grid: {
      top: premium ? '18%' : '16%',
      bottom: 56,
      left: 68,
      right: 28
    },
    tooltip: {
      trigger: 'axis',
      backgroundColor: premium ? 'rgba(15, 23, 42, 0.92)' : 'rgba(15, 23, 42, 0.88)',
      borderWidth: 0,
      textStyle: {
        color: '#ffffff'
      },
      axisPointer: {
        type: 'line',
        label: {
          show: true
        }
      }
    },
    legend: {
      right: 20,
      top: 16,
      itemWidth: 14,
      itemHeight: 8,
      textStyle: {
        color: '#475569'
      }
    },
    dataZoom: [
      {
        type: 'inside',
        filterMode: 'none'
      },
      {
        type: 'slider',
        height: 22,
        bottom: 14,
        borderColor: 'transparent',
        backgroundColor: premium ? 'rgba(148, 163, 184, 0.16)' : 'rgba(148, 163, 184, 0.14)',
        fillerColor: premium ? 'rgba(59, 130, 246, 0.18)' : 'rgba(59, 130, 246, 0.14)',
        handleStyle: {
          color: metric.color
        }
      }
    ],
    xAxis: {
      type: 'category',
      boundaryGap: false,
      data: xData,
      axisLine: {
        show: true,
        lineStyle: {
          color: '#cbd5e1'
        }
      },
      axisTick: {
        show: false
      },
      axisLabel: {
        color: '#64748b',
        hideOverlap: true
      }
    },
    yAxis: {
      type: 'value',
      name: metric.unit ? `${metric.label}(${metric.unit})` : metric.label,
      nameTextStyle: {
        color: '#475569'
      },
      splitLine: {
        lineStyle: {
          color: 'rgba(148, 163, 184, 0.18)'
        }
      },
      axisTick: {
        show: false
      },
      axisLine: {
        show: true,
        lineStyle: {
          color: '#cbd5e1'
        }
      },
      axisLabel: {
        color: '#64748b'
      }
    },
    series: [
      {
        name: metric.unit ? `${metric.label}(${metric.unit})` : metric.label,
        type: 'line',
        smooth: true,
        connectNulls: true,
        showSymbol: records.length <= 80,
        symbol: 'circle',
        symbolSize: premium ? 7 : 6,
        lineStyle: {
          width: premium ? 3 : 2,
          color: metric.color
        },
        itemStyle: {
          color: metric.color,
          borderColor: '#ffffff',
          borderWidth: premium ? 2 : 1
        },
        areaStyle: {
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: metric.areaStartColor },
            { offset: 1, color: metric.areaEndColor }
          ])
        },
        emphasis: {
          focus: 'series'
        },
        data: seriesData
      }
    ]
  };
};

export const connectCurveCharts = (
  charts: Array<
    | {
        chart?: unknown;
      }
    | null
    | undefined
  >,
  groupId: string
) => {
  const chartInstances = charts
    .map((item) => item?.chart)
    .filter((item): item is echarts.ECharts => Boolean(item) && typeof (item as echarts.ECharts).setOption === 'function');

  if (chartInstances.length < 2) {
    return () => undefined;
  }

  chartInstances.forEach((item) => {
    item.group = groupId;
  });
  echarts.connect(groupId);

  return () => {
    echarts.disconnect(groupId);
    chartInstances.forEach((item) => {
      if (item.group === groupId) {
        item.group = '';
      }
    });
  };
};
