<template>
  <view class="income-curve-card">
    <text class="curve-title">七日收益曲线</text>
    <image
        class="chart-bg"
        src="/static/dashboard/ucharts-bj.png"
        mode="scaleToFill"
      />
    <!-- 错误态 -->
    <view v-if="error" class="error-box" @tap="$emit('retry')">
      <text class="error-text">加载失败，点击重试</text>
    </view>

    <!-- 骨架态 -->
    <view
      v-else-if="loading || !points || points.length === 0"
      class="skeleton-chart"
    ></view>

    <!-- 图表 -->
    <view v-else class="chart-wrap">
      <view class="chart-canvas" @mousemove="onCanvasMouseMove">
        <qiun-data-charts
          type="area"
          :opts="chartOpts"
          :chartData="chartData"
          :ontouch="true"
          :ontap="true"
          :onmouse="true"
          :onmovetip="true"
          :in-scroll-view="true"
          :tooltipShow="true"
          :tooltipFormat="'incomeFormatter'"
          :canvas2d="false"
          canvasId="incomeCurveCanvas"
          :canvasHeight="240"
          @complete="onChartComplete"
        />
      </view>
    </view>
  </view>
</template>

<script setup>
import { computed, getCurrentInstance, nextTick, ref } from "vue";
// 显式 import 兜底，避免 easycom 漏注册
import QiunDataCharts from "@/uni_modules/qiun-data-charts/components/qiun-data-charts/qiun-data-charts.vue";
// [新增] 共享 u-charts config 拿到 cfu.instance[canvasId] 直接调 showToolTip
//   （绕开 qiun-data-charts 的私有 _showTooltip：它通过 Vue ref 不可达）
import cfu from "@/uni_modules/qiun-data-charts/js_sdk/u-charts/config-ucharts.js";

const props = defineProps({
  points: { type: Array, default: () => [] },
  loading: { type: Boolean, default: false },
  error: { type: String, default: null },
});
defineEmits(["retry"]);

// [新增] 上次已展示 tooltip 的数据点数量，避免重复触发
const lastShownLen = ref(0);

// [新增] 与 template <qiun-data-charts> 的 canvasId prop 保持一致
//   用于从 cfu.instance 中拿到对应的 uCharts 实例
const CANVAS_ID = "incomeCurveCanvas";

// [新增] 自定义 tooltip 文本格式：把 series.data 包成 "+xxx"
//   说明：qiun-data-charts 的 tooltipCustom prop 类型是 Object 而非 Function
//   （它期望 {x, y, index, textList} 字段，不会被当函数调用），所以走原生
//   formatter 通道：注册到 cfu.formatter，再用 :tooltipFormat="..." 让 chart
//   组件 _showTooltip / renderjs showTooltip 用我们注册的函数。
//   我们的 direct showToolTip 调用（默认显示、鼠标移动）也显式传这个 formatter。
//   日期通过 extra.tooltip.showCategory=true 自动作为第一行（来自 opts.categories）。
const incomeFormatter = (item, _category, _index, _opts) => `+${item.data}`;
cfu.formatter.incomeFormatter = incomeFormatter;

// [新增] 图表渲染完成回调：
//   拿最后一点的真实屏幕坐标，构造 fake event 喂给 u-charts 的 showToolTip，
//   让 tooltip 默认显示在最后一点。后续用户触摸其他点仍由 qiun-data-charts
//   内部 _tap → _showTooltip 流程处理（不走我们这里）。
//
// 实现说明：
//   1. calPoints 是 canvas 像素 (=CSS像素 × opts.pix)；showToolTip 内部
//      getTouches 会再乘一次 opts.pix 转换为 canvas 像素，所以先除回去
//      避免双倍缩放。真实触摸路径里 e.detail.x 是 CSS 像素，getTouches
//      一次乘 pix 转换到 canvas 像素。
//   2. 通过共享 config-ucharts.js 拿到 cfu.instance[CANVAS_ID] 直接调
//      showToolTip，绕开 qiun-data-charts 私有 _showTooltip（Vue ref 不可达）
//   3. option.index 显式传最后一点索引，不再依赖 getCurrentDataIndex 推断
const onChartComplete = async (e) => {
  console.log("mouse move", e)
  const len = props.points.length;
  if (!len) return;
  // 数据点数量未变化则不重复触发（防 complete 事件多次触发）
  if (lastShownLen.value === len) return;
  await nextTick();
  const series = e?.opts?.chartData?.calPoints?.[0];
  if (!series || !series[len - 1]) return;
  const pix = e?.opts?.pix || 1;
  const { x, y } = series[len - 1];
  const fakeE = { changedTouches: [{ x: x / pix, y: y / pix }] };
  try {
    const instance = cfu.instance[CANVAS_ID];
    if (!instance) {
      console.warn("[IncomeCurve] uCharts instance not found for canvas:", CANVAS_ID);
      return;
    }
    // [H5 fix] qiun-data-charts 的 cfu.option[cid].onmouse 在 HBuilderX H5 预览下
    //   保持 undefined（看 uni_modules 源码 mounted() 第 488-490 行：仅 inWin 时赋值）。
    //   它的 renderjs.mouseMove 第一行 `if(onmouse == false) return;` 因
    //   `undefined == false` 为 true 而直接退出，导致 H5 鼠标移动不切换 tooltip。
    //   这里显式设为 true 开启 H5 鼠标响应。
    if (cfu.option[CANVAS_ID]) {
      cfu.option[CANVAS_ID].onmouse = true;
    }
    // [诊断日志] 验证 onmouse 修复 + showToolTip 调用
    console.log("[IncomeCurve] onmouse patched to:", cfu.option[CANVAS_ID]?.onmouse, "instance:", !!instance, "len:", len);
    // 直接调 u-charts 的 showToolTip，option.index 显式传最后一点；
    // formatter 显式传 incomeFormatter 让 income 显示成 "+xxx"
    instance.showToolTip(fakeE, { index: len - 1, formatter: incomeFormatter });
  } catch (err) {
    console.warn("[IncomeCurve] show default tooltip failed:", err);
  }
  lastShownLen.value = len;
};

// [新增] H5 鼠标移动响应（绕开 qiun-data-charts renderjs 路径）
//   问题：uni-app 的 renderjs 层不把 mousemove 事件转发到 renderjs 脚本，
//   所以 rdcharts.mouseMove 永远不会被调用，H5 桌面端鼠标移动不能切换 tooltip。
//   解决：直接在 IncomeCurve 上挂 @mousemove 监听，用 cfu.instance 直接调
//   u-charts 原生 showToolTip（同样支持 getCurrentDataIndex 自动找最近数据点）。
//   非 H5 平台无 mousemove 事件，本 handler 是 no-op。
//   节流到 60ms 一次，避免每次 mousemove 都重绘 canvas。
let lastMouseMoveTime = 0;
let mouseMoveCallCount = 0;
const onCanvasMouseMove = (e) => {
  mouseMoveCallCount++;
  if (mouseMoveCallCount === 1) {
    // [诊断日志] 第一次触发时打印，证明事件到了
    console.log("[IncomeCurve] onCanvasMouseMove FIRST CALL — event reached Vue layer, clientX:", e.clientX, "clientY:", e.clientY);
  }
  const uchartsInstance = cfu.instance[CANVAS_ID];
  if (!uchartsInstance) return;
  const now = Date.now();
  if (now - lastMouseMoveTime < 60) return; // 节流 60ms
  lastMouseMoveTime = now;
  // 捕获事件坐标（callback 是异步的，那时 e 可能已变）
  const clientX = e.clientX;
  const clientY = e.clientY;
  // qiun-data-charts 内部 view id 是 'UC' + canvasId
  const viewSel = "#UC" + CANVAS_ID;
  const query = uni.createSelectorQuery();
  const inst = getCurrentInstance();
  if (inst) query.in(inst.proxy);
  query.select(viewSel).boundingClientRect((rect) => {
    if (!rect) return;
    const localX = clientX - rect.left;
    const localY = clientY - rect.top;
    // 只在图表范围内才响应
    if (localX < 0 || localX > rect.width || localY < 0 || localY > rect.height) return;
    const fakeE = { changedTouches: [{ x: localX, y: localY }] };
    try {
      // 不传 index，让 u-charts 内部 getCurrentDataIndex 自动找最近点；
      // formatter 显式传 incomeFormatter 让 income 显示成 "+xxx"
      uchartsInstance.showToolTip(fakeE, { formatter: incomeFormatter });
    } catch (err) {
      // 鼠标在某些区域可能让 getCurrentDataIndex 返回 -1，showToolTip 内部会忽略
    }
  }).exec();
};

const chartData = computed(() => {
  const categories = props.points.map((p) => p.date.slice(5));
  const lineData = props.points.map((p) => p.income);
  return {
    categories,
    series: [
      {
        name: "收益",
        type: "area",
        data: lineData,
        color: "#1BAFEE",
        // [自定义] u-charts.js 已 patch：series.linearColor 启用横向多色渐变
        // 对应 CSS: linear-gradient(90deg, #D1EFFFBC 0%, #E7E8A9D8 49.52%, #FF9CE5FA 100%)
        linearColor: [
          [0, "rgba(209, 239, 255, 0.735)"],
          [0.4952, "rgba(231, 232, 169, 0.846)"],
          [1, "rgba(255, 156, 229, 0.98)"],
        ],
        linearDirection: "horizontal", // 'horizontal'(默认) | 'vertical'
      },
    ],
  };
});

const PADDING = [16, -16, 8, -16]; // top, right, bottom, left

const chartOpts = {
  color: ["#1BAFEE"],
  padding: PADDING,
  dataLabel: false,
  legend: { show: false },
  // 关闭曲线上每个数据点的小圆圈
  dataPointShape: false,
  xAxis: {
    disabled: true,
    disableGrid: true,
    axisLine: false,
    fontColor: "#FFFFFF",
    fontSize: 9,
  },
  yAxis: {
    disabled: true,
    showTitle: false,
    data: [{ min: 0, disabled: true, axisLine: false }],
    disableGrid: true,
    fontColor: "#FFFFFF",
    fontSize: 9,
  },
  extra: {
    tooltip: {
      showBox: true,
      showArrow: true,
      // [修复] showCategory=true 让日期（来自 opts.categories）作为 tooltip 第一行；
      //   qiun-data-charts 的 tooltipCustom prop 是 Object 不是 Function，函数版根本不被调用。
      //   所以改用 u-charts 原生 showCategory 通道 + cfu.formatter 自定义收入格式。
      showCategory: true,
      bgColor: "#000000",
      bgOpacity: 0.6,
      // [修复] fontColor 控制第一行（日期）的颜色：灰色
      //   第二行（收入）由 series.color 控制（#1BAFEE 蓝色），不受 fontColor 影响
      fontColor: "#999999",
      fontSize: 11,
      splitLine: true,
      horizentalLine: { type: "dash", width: 1, color: "#FFFFFF" },
    },
    // area 类型曲线区域图，搭配 series.linearType=custom + linearColor 实现自定义多色渐变
    // addLine:false 不绘制曲线（曲线透明），仅保留渐变面积填充
    area: {
      type: "curve",
      opacity: 1,
      addLine: false,
      width: 2,
      gradient: true,
      activeType: "hollow",
    },
  },
};
</script>

<style lang="scss" scoped>
.income-curve-card {
  background: linear-gradient(
    107.96deg,
    rgba(255, 223, 119, 0.59) -28.33%,
    rgba(142, 149, 226, 0.59) 44.4%,
    rgba(244, 140, 255, 0.59) 117.77%
  );
  border-radius: 22rpx;
  padding: 24rpx;
  box-sizing: border-box; // padding 计入总高度，避免内部撑爆
  box-shadow: 0px 4px 4px 0px #BD323240;
  position: relative;
  height: 256rpx;
  display: flex;
  flex-direction: column;
  overflow: hidden; // 裁掉超出圆角的部分（包括背景图）
}

.curve-title {
  display: block;
  font-size: 28rpx;
  font-weight: 600;
  color: #ffffff;
  margin-bottom: 4rpx; // 卡片高度有限，缩小标题与图表间距
  text-shadow: 0px 2px 2px rgba(0, 0, 0, 0.2);
  position: relative;
  z-index: 2; // 标题在背景图和图表之上
}

.chart-wrap {
  position: relative;
  flex: 1; // 撑满标题下方的剩余空间
  min-height: 0; // flex 子项防止溢出的兜底
  border-radius: 17rpx;
  overflow: hidden;
  z-index: 1;
  // backdrop-filter: blur(10px);
}

.chart-bg {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 224rpx;
  height: 100%;
  z-index: 0; // 在标题(z:2)和图表(z:1)之下
  pointer-events: none; // 不拦截 tap，事件穿透到 canvas
  opacity: 0.4;
}

.chart-canvas {
  position: relative;
  width: 100%;
  height: 100%; // 跟随 .chart-wrap 撑满
  // 阴影：drop-shadow 沿 canvas 内容 alpha 通道生效，跟随曲线形状
  // 对应 CSS: box-shadow: -1px -5px 4px 0px #CF232338  (#CF2323 + alpha 0x38 ≈ 0.22)
  filter: drop-shadow(-1px -5px 4px rgba(207, 35, 35, 0.22));
}

.skeleton-chart {
  height: 360rpx;
  border-radius: 17rpx;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0.1) 25%,
    rgba(255, 255, 255, 0.2) 50%,
    rgba(255, 255, 255, 0.1) 75%
  );
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.5s infinite;
}

.error-box {
  height: 360rpx;
  border-radius: 17rpx;
  background: rgba(255, 100, 100, 0.15);
  border: 2rpx solid rgba(255, 100, 100, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}

.error-text {
  color: #ff8080;
  font-size: 28rpx;
}

@keyframes skeleton-shimmer {
  0% {
    background-position: 200% 0;
  }
  100% {
    background-position: -200% 0;
  }
}
</style>
