/**
 * Annulus geometry for the two breakdown donuts. Pure, so the arithmetic that
 * decides whether a slice closes correctly is testable without a DOM.
 */

export interface DonutSlice {
	/** Semantic identity: the React key, the colour key and the legend's identity. */
	key: string;
	label: string;
	/** Disambiguates entries whose labels collide — two "Unknown"s, one name on two route kinds. */
	secondary?: string;
	value: number;
	color: string;
}

export interface DonutArc {
	slice: DonutSlice;
	/** Of the whole, 0 to 1. */
	share: number;
	d: string;
}

export interface DonutLayout {
	size: number;
	total: number;
	arcs: DonutArc[];
}

const START_ANGLE = -Math.PI / 2;

function point(center: number, radius: number, angle: number): string {
	return `${(center + radius * Math.cos(angle)).toFixed(3)} ${(center + radius * Math.sin(angle)).toFixed(3)}`;
}

/**
 * A whole-circle slice cannot be drawn as one arc — start and end coincide, and
 * the renderer draws nothing at all — so the ring is two half arcs.
 */
function fullRing(center: number, outer: number, inner: number): string {
	const top = `${center} ${center - outer}`;
	const bottom = `${center} ${center + outer}`;
	const innerTop = `${center} ${center - inner}`;
	const innerBottom = `${center} ${center + inner}`;
	return [
		`M ${top}`,
		`A ${outer} ${outer} 0 0 1 ${bottom}`,
		`A ${outer} ${outer} 0 0 1 ${top}`,
		`M ${innerTop}`,
		`A ${inner} ${inner} 0 0 0 ${innerBottom}`,
		`A ${inner} ${inner} 0 0 0 ${innerTop}`,
		"Z",
	].join(" ");
}

/**
 * Slices in the order given — the caller has already ranked them — starting at
 * twelve o'clock and running clockwise. Zero-valued slices are dropped: they
 * have no arc to draw, and a legend entry reading 0 is noise.
 */
export function layoutDonut(slices: DonutSlice[], size: number, thickness: number): DonutLayout {
	const drawn = slices.filter((slice) => slice.value > 0);
	const total = drawn.reduce((sum, slice) => sum + slice.value, 0);
	if (total <= 0) return { size, total: 0, arcs: [] };

	const center = size / 2;
	const outer = center;
	const inner = Math.max(0, center - thickness);

	if (drawn.length === 1) {
		return {
			size,
			total,
			arcs: [{ slice: drawn[0], share: 1, d: fullRing(center, outer, inner) }],
		};
	}

	let angle = START_ANGLE;
	const arcs = drawn.map((slice) => {
		const share = slice.value / total;
		const sweep = share * Math.PI * 2;
		const end = angle + sweep;
		const large = sweep > Math.PI ? 1 : 0;
		const d = [
			`M ${point(center, outer, angle)}`,
			`A ${outer} ${outer} 0 ${large} 1 ${point(center, outer, end)}`,
			`L ${point(center, inner, end)}`,
			`A ${inner} ${inner} 0 ${large} 0 ${point(center, inner, angle)}`,
			"Z",
		].join(" ");
		angle = end;
		return { slice, share, d };
	});

	return { size, total, arcs };
}
