import dayjs from "dayjs";

const RISK_CALENDAR_MONTH_TOTAL = 12;

const RISK_HEAT_CELL_INNER = 16;
const RISK_HEAT_CELL_GAP = 2;
const RISK_HEAT_CELL_STRIDE = RISK_HEAT_CELL_INNER + RISK_HEAT_CELL_GAP;
const RISK_HEAT_CELL_BORDER = RISK_HEAT_CELL_GAP / 2;

const RISK_HEAT_CELL_SEPARATOR_COLOR = "rgb(255, 255, 255)";
const RISK_HEAT_EMPTY_FILL = "rgb(247, 248, 249)";

/** 每行 5 列；日期按「左→右、上→下」排布（不再用 6 列×6 行） */
const COLS_PER_MONTH = 5;

/**
 * 行数 = ceil(天数/5)；月初补 (行数*5-天数) 个空位，使最后一行尽量排满。
 * 常见：28–30 天为 5×6 内；31 天需 7 行（5×6=30 格装不下 31 天）。
 * @param {number} daysInMonth
 */
const getMonthGridLayout = (daysInMonth) => {
	const rows = Math.ceil(daysInMonth / COLS_PER_MONTH);
	const paddingSlots = rows * COLS_PER_MONTH - daysInMonth;
	return { rows, paddingSlots };
};

const HEAT_RGB_STOPS = [
	[231, 243, 255],
	[137, 193, 255],
	[5, 95, 194]
];

const lerpChannel = (a, b, t) => Math.round(a + (b - a) * t);

/** v<=0 用空色；v>0 按三段渐变 */
const getHeatColor = (v, maxV) => {
	const n = Number(v);
	if (!Number.isFinite(n) || n <= 0) {
		return RISK_HEAT_EMPTY_FILL;
	}
	const ratio = Math.min(n / maxV, 1);
	const [c0, c1, c2] = HEAT_RGB_STOPS;
	if (ratio <= 0.5) {
		const t = ratio / 0.5;
		return `rgb(${lerpChannel(c0[0], c1[0], t)},${lerpChannel(c0[1], c1[1], t)},${lerpChannel(c0[2], c1[2], t)})`;
	}
	const t = (ratio - 0.5) / 0.5;
	return `rgb(${lerpChannel(c1[0], c2[0], t)},${lerpChannel(c1[1], c2[1], t)},${lerpChannel(c1[2], c2[2], t)})`;
};

const getCalendarHeatChart = (rawData) => {
	const now = dayjs();
	const monthLabels = [];
	for (let offset = RISK_CALENDAR_MONTH_TOTAL - 1; offset >= 0; offset -= 1) {
		monthLabels.push(now.subtract(offset, "month").format("YYYY-MM"));
	}

	const startBound = dayjs(`${monthLabels[0]}-01`).startOf("month");
	const endBound = now.endOf("month");

	const countMap = new Map();
	let maxVal = 0;
	for (const row of rawData || []) {
		if (!row || row.length < 2) continue;
		const d = dayjs(row[0]);
		if (!d.isValid()) continue;
		if (d.isBefore(startBound, "day") || d.isAfter(endBound, "day")) continue;
		const key = d.format("YYYY-MM-DD");
		const c = Number(row[1]) || 0;
		countMap.set(key, c);
		if (c > maxVal) maxVal = c;
	}
	const visualMax = Math.max(1, maxVal);

	const monthWidthPx = COLS_PER_MONTH * RISK_HEAT_CELL_STRIDE;

	const monthLayouts = monthLabels.map((ym) => {
		const daysInMonth = dayjs(`${ym}-01`).daysInMonth();
		return {
			ym,
			daysInMonth,
			...getMonthGridLayout(daysInMonth)
		};
	});

	/** 12 个月紧挨排列；月与月之间不额外加空隙，仅靠两侧 1px 描边形成固定 2px */
	const CONTENT_MARGIN_LEFT_PX = 22;
	const CONTENT_MARGIN_TOP_PX = 15;
	/** 月份文字在上，格子在其下（12px 字号约一行高） */
	const MONTH_LABEL_TOP_PX = CONTENT_MARGIN_TOP_PX;
	const GRID_TOP_PX = CONTENT_MARGIN_TOP_PX + 18;
	const monthTitleGraphics = monthLayouts.map((layout, idx) => {
		const monthNum = dayjs(`${layout.ym}-01`).month() + 1;
		return {
			type: "text",
			id: `risk-heat-month-${idx}`,
			left: CONTENT_MARGIN_LEFT_PX + idx * monthWidthPx,
			top: MONTH_LABEL_TOP_PX,
			z: 10,
			silent: true,
			style: {
				text: `${monthNum}月`,
				fill: "rgb(59, 65, 75)",
				fontSize: 12,
				fontWeight: 500,
				fontFamily: "Source Han Sans CN, PingFang SC, Microsoft YaHei, sans-serif"
			}
		};
	});

	const grids = monthLayouts.map((layout, idx) => ({
		left: CONTENT_MARGIN_LEFT_PX + idx * monthWidthPx,
		top: GRID_TOP_PX,
		width: monthWidthPx,
		height: layout.rows * RISK_HEAT_CELL_STRIDE,
		containLabel: false
	}));

	const xAxes = monthLabels.map((_, idx) => ({
		type: "value",
		gridIndex: idx,
		min: 0,
		max: COLS_PER_MONTH,
		axisLine: { show: false },
		axisTick: { show: false },
		axisLabel: { show: false },
		splitLine: { show: false }
	}));

	const yAxes = monthLayouts.map((layout, idx) => ({
		type: "value",
		gridIndex: idx,
		min: 0,
		max: layout.rows,
		axisLine: { show: false },
		axisTick: { show: false },
		axisLabel: { show: false },
		splitLine: { show: false }
	}));

	const series = monthLayouts.map((layout, idx) => {
		const { ym, daysInMonth, rows, paddingSlots } = layout;
		const m = dayjs(`${ym}-01`);
		const data = [];
		const totalSlots = rows * COLS_PER_MONTH;
		for (let slot = 0; slot < totalSlots; slot += 1) {
			const col = slot % COLS_PER_MONTH;
			const row = Math.floor(slot / COLS_PER_MONTH);
			if (slot < paddingSlots) {
				data.push([col, row, "", null, 1]);
				continue;
			}
			const day = slot - paddingSlots + 1;
			const dateStr = m.date(day).format("YYYY-MM-DD");
			const v = countMap.get(dateStr) ?? 0;
			data.push([col, row, dateStr, v, 0]);
		}

		return {
			type: "custom",
			name: ym,
			xAxisIndex: idx,
			yAxisIndex: idx,
			data,
			renderItem(params, api) {
				const col = api.value(0);
				const row = api.value(1);
				const dateStr = api.value(2);
				const v = api.value(3);
				const isPadding = api.value(4) === 1;
				/**
				 * 不能用 api.coord([0,0]) 当「顶部」：默认 value 轴 y=0 在网格下方，
				 * 会导致整块热力画在偏下位置并从 canvas 底部被裁切。
				 * 用当前 grid 的 coordSys 像素框，按列/行直接铺格，保证第 0 行贴 grid 顶。
				 */
				const coordSys = params.coordSys;
				if (!coordSys || typeof coordSys.x !== "number" || typeof coordSys.y !== "number") {
					return null;
				}
				const xPx = coordSys.x + col * RISK_HEAT_CELL_STRIDE;
				const yPx = coordSys.y + row * RISK_HEAT_CELL_STRIDE;

				return {
					type: "rect",
					shape: {
						x: xPx,
						y: yPx,
						width: RISK_HEAT_CELL_STRIDE,
						height: RISK_HEAT_CELL_STRIDE
					},
					style: api.style({
						fill: isPadding ? RISK_HEAT_EMPTY_FILL : getHeatColor(v, visualMax),
						stroke: RISK_HEAT_CELL_SEPARATOR_COLOR,
						lineWidth: RISK_HEAT_CELL_BORDER
					}),
					silent: isPadding
				};
			},
			encode: {
				tooltip: [2, 3]
			},
			tooltip: {
				formatter(p) {
					const dateStr = p.data?.[2] || "";
					if (!dateStr) {
						return "";
					}
					const v = p.data?.[3] ?? 0;
					return `${dateStr}<br/>风险数量: ${v}`;
				}
			}
		};
	});

	return {
		tooltip: {
			trigger: "item",
			position: "top"
		},
		graphic: monthTitleGraphics,
		grid: grids,
		xAxis: xAxes,
		yAxis: yAxes,
		series
	};
};

export default getCalendarHeatChart;