import type { ScreenData, TimeRange } from './screen';
import { demoScreen } from './screen-demo';
import {
	FieldTracker,
	KPI_GROUPS,
	groupHasLive,
	resolveDataMode,
	type ScreenDataMode
} from './screen-field-meta';
import { structureScreen } from './screen-structure';

export type { ScreenDataMode };

export interface ResolvedScreenData {
	data: ScreenData;
	mode: ScreenDataMode;
	demoFields: string[];
	liveFields: string[];
}

function sum(nums: number[]) {
	return nums.reduce((a, b) => a + (Number(b) || 0), 0);
}

function hasPositive(nums: number[]) {
	return nums.some(v => Number(v) > 0);
}

function hasRankData(list: any[]) {
	return list.length > 0 && list.some(e => Number(e?.people) > 0 || Number(e?.weight) > 0);
}

function pickKpis(
	live: Record<string, number> | undefined,
	demo: Record<string, number>,
	ctx: FieldTracker
) {
	const out = { ...demo };
	if (!live) {
		Object.values(KPI_GROUPS)
			.flat()
			.forEach(key => ctx.markDemo(`kpis.${key}`));
		return out;
	}

	for (const [group, keys] of Object.entries(KPI_GROUPS)) {
		if (groupHasLive(keys, live)) {
			keys.forEach(key => {
				const v = Number(live[key]);
				out[key] = Number.isFinite(v) ? v : demo[key];
				ctx.markLive(`kpis.${key}`);
			});
		} else {
			keys.forEach(key => ctx.markDemo(`kpis.${key}`));
		}
	}
	return out;
}

function pickArray<T>(
	field: string,
	live: T[] | undefined,
	demo: T[],
	ctx: FieldTracker,
	valid: (v: T[]) => boolean
) {
	if (live && valid(live)) {
		ctx.markLive(field);
		return live;
	}
	ctx.markDemo(field);
	return demo;
}

function pickTrend(
	field: string,
	live: { weight: number[]; value: number[]; carbon: number[] } | undefined,
	demo: ScreenData['trend'],
	axis: string[],
	ctx: FieldTracker
) {
	const valid =
		live &&
		live.weight?.length &&
		(sum(live.weight) > 0 || sum(live.value) > 0 || sum(live.carbon) > 0);
	if (valid) {
		ctx.markLive(field);
		return {
			weight: alignSeries(live!.weight, axis.length, live!.weight[live!.weight.length - 1] || 0),
			value: alignSeries(live!.value, axis.length, live!.value[live!.value.length - 1] || 0),
			carbon: alignSeries(live!.carbon, axis.length, live!.carbon[live!.carbon.length - 1] || 0)
		};
	}
	ctx.markDemo(field);
	return alignTrend(demo, axis.length);
}

function alignSeries(source: number[], len: number, fill = 0) {
	if (!source?.length) return Array.from({ length: len }, () => fill);
	if (source.length === len) return [...source];
	if (source.length > len) return source.slice(source.length - len);
	return [...Array.from({ length: len - source.length }, () => fill), ...source];
}

function alignTrend(trend: ScreenData['trend'], len: number) {
	return {
		weight: alignSeries(trend.weight, len),
		value: alignSeries(trend.value, len),
		carbon: alignSeries(trend.carbon, len)
	};
}

function pickWasteMix(
	live: ScreenData['wasteMix'] | undefined,
	demo: ScreenData['wasteMix'],
	ctx: FieldTracker
) {
	if (
		live?.length &&
		live.some(e => Number(e.weight) > 0 || Number(e.count) > 0 || Number(e.value) > 0)
	) {
		ctx.markLive('wasteMix');
		return live.map((item, i) => ({
			...demo[i],
			...item,
			color: item.color || demo[i]?.color
		}));
	}
	ctx.markDemo('wasteMix');
	return demo;
}

function pickRecycleItems(
	live: ScreenData['recycleItems'] | undefined,
	demo: ScreenData['recycleItems'],
	ctx: FieldTracker
) {
	if (live?.length && live.some(e => Number(e.weight) > 0 || Number(e.value) > 0 || Number(e.carbon) > 0)) {
		ctx.markLive('recycleItems');
		return live.map((item, i) => ({
			...demo[i],
			...item,
			color: item.color || demo[i]?.color
		}));
	}
	ctx.markDemo('recycleItems');
	return demo;
}

function pickPointFlow(
	live: ScreenData['pointFlow'] | undefined,
	demo: ScreenData['pointFlow'],
	ctx: FieldTracker
) {
	const out = { ...demo };
	if (!live) {
		ctx.markDemo('pointFlow');
		(['issued', 'transfer', 'donate', 'mall', 'balance'] as const).forEach(key =>
			ctx.markDemo(`pointFlow.${key}`)
		);
		return out;
	}

	let hasLive = false;
	(['issued', 'transfer', 'donate', 'mall', 'balance'] as const).forEach(key => {
		if (Number(live[key]) > 0) {
			out[key] = live[key];
			ctx.markLive(`pointFlow.${key}`);
			hasLive = true;
		} else {
			ctx.markDemo(`pointFlow.${key}`);
		}
	});
	if (hasLive) ctx.markLive('pointFlow');
	else ctx.markDemo('pointFlow');
	return out;
}

function pickOrdersByType(
	live: ScreenData['ordersByType'] | undefined,
	demo: ScreenData['ordersByType'],
	ctx: FieldTracker
) {
	const keys = ['现场回收', '预约上门', '大屏投递', '活动回收'] as const;
	const liveValid = live?.axis?.length && keys.some(k => hasPositive(live[k] || []));
	if (liveValid) {
		ctx.markLive('ordersByType');
		return live!;
	}
	ctx.markDemo('ordersByType');
	return demo;
}

function pickAppointmentFlow(
	live: ScreenData['appointmentFlow'] | undefined,
	demo: ScreenData['appointmentFlow'],
	ctx: FieldTracker
) {
	if (live?.length && live.some(e => Number(e.value) > 0)) {
		ctx.markLive('appointmentFlow');
		return live;
	}
	ctx.markDemo('appointmentFlow');
	return demo;
}

function pickSorterStatus(
	live: ScreenData['sorterStatus'] | undefined,
	demo: ScreenData['sorterStatus'],
	ctx: FieldTracker
) {
	if (live?.length && sum(live.map(e => Number(e.value))) > 0) {
		ctx.markLive('sorterStatus');
		return live;
	}
	ctx.markDemo('sorterStatus');
	return demo;
}

function pickAxis(
	live: ScreenData | null | undefined,
	demo: ScreenData,
	structure: ScreenData,
	ctx: FieldTracker
) {
	const useLiveAxis =
		live?.axis?.length &&
		(sum(live.trend?.weight || []) > 0 ||
			sum(live.participateTrend || []) > 0 ||
			sum(live.deliverTrend || []) > 0 ||
			live.axis.length > 1);
	if (useLiveAxis) {
		ctx.markLive('axis');
		return live!.axis;
	}
	ctx.markDemo('axis');
	return demo.axis.length ? demo.axis : structure.axis;
}

/** 结构 + 演示 + 真实数据按字段/分组合并 */
export function resolveScreenData(live: ScreenData | null | undefined, range: TimeRange): ResolvedScreenData {
	const structure = structureScreen(range);
	const demo = demoScreen(range);
	const ctx = new FieldTracker();

	const axis = pickAxis(live, demo, structure, ctx);
	const kpis = pickKpis(live?.kpis, demo.kpis, ctx);
	const wasteMix = pickWasteMix(live?.wasteMix, demo.wasteMix, ctx);
	const recycleItems = pickRecycleItems(live?.recycleItems, demo.recycleItems, ctx);
	const trend = pickTrend('trend', live?.trend, demo.trend, axis, ctx);

	let yearCarbon = demo.yearCarbon;
	if (live?.yearCarbon?.length && sum(live.yearCarbon) > 0) {
		yearCarbon = live.yearCarbon;
		ctx.markLive('yearCarbon');
	} else {
		ctx.markDemo('yearCarbon');
	}

	const deptRank = pickArray('deptRank', live?.deptRank, demo.deptRank, ctx, hasRankData);
	const classRank = pickArray('classRank', live?.classRank, demo.classRank, ctx, hasRankData);
	const majorRank = pickArray('majorRank', live?.majorRank, demo.majorRank, ctx, hasRankData);
	const pointFlow = pickPointFlow(live?.pointFlow, demo.pointFlow, ctx);

	let participateTrend = alignSeries(demo.participateTrend, axis.length);
	if (live?.participateTrend?.length && sum(live.participateTrend) > 0) {
		participateTrend = alignSeries(live.participateTrend, axis.length);
		ctx.markLive('participateTrend');
	} else {
		ctx.markDemo('participateTrend');
	}

	let deliverTrend = alignSeries(demo.deliverTrend, axis.length);
	if (live?.deliverTrend?.length && sum(live.deliverTrend) > 0) {
		deliverTrend = alignSeries(live.deliverTrend, axis.length);
		ctx.markLive('deliverTrend');
	} else {
		ctx.markDemo('deliverTrend');
	}

	const ordersByType = pickOrdersByType(live?.ordersByType, demo.ordersByType, ctx);
	const appointmentFlow = pickAppointmentFlow(live?.appointmentFlow, demo.appointmentFlow, ctx);
	const sorterStatus = pickSorterStatus(live?.sorterStatus, demo.sorterStatus, ctx);
	const vehicles = pickArray('vehicles', live?.vehicles, demo.vehicles, ctx, v => v.length > 0);
	const sites = pickArray('sites', live?.sites, demo.sites, ctx, v => v.length > 0);

	const data: ScreenData = {
		range,
		axis,
		kpis,
		wasteMix,
		recycleItems,
		trend,
		yearCarbon,
		deptRank,
		classRank,
		majorRank,
		pointFlow,
		participateTrend,
		deliverTrend,
		ordersByType,
		appointmentFlow,
		sorterStatus,
		vehicles,
		sites,
		unavailable: live?.unavailable || []
	};

	return {
		data,
		mode: resolveDataMode(live, ctx),
		demoFields: ctx.listDemo(),
		liveFields: ctx.listLive()
	};
}

/** 接口失败或无 live 数据时的初始展示 */
export function initialScreenData(range: TimeRange = 'month') {
	return resolveScreenData(null, range);
}

export { structureScreen, buildScreenAxis } from './screen-structure';
export { demoScreen } from './screen-demo';
