import {
	getDefaultLeftHandle,
	getDefaultRightHandle,
} from "@/animation/bezier";
import type {
	CurveHandle,
	NormalizedCubicBezier,
	ScalarAnimationKey,
} from "@/animation/types";

const VALUE_EPSILON = 1e-6;

function clamp01({ value }: { value: number }): number {
	return Math.max(0, Math.min(1, value));
}

export function getNormalizedCubicBezierForScalarSegment({
	leftKey,
	rightKey,
	referenceSpanValue,
}: {
	leftKey: ScalarAnimationKey;
	rightKey: ScalarAnimationKey;
	/** Fallback Y-axis scale used when the segment is flat (spanValue ≈ 0). */
	referenceSpanValue?: number;
}): NormalizedCubicBezier | null {
	const spanTime = rightKey.time - leftKey.time;
	const spanValue = rightKey.value - leftKey.value;
	const effectiveSpanValue =
		Math.abs(spanValue) > VALUE_EPSILON
			? spanValue
			: referenceSpanValue !== undefined && Math.abs(referenceSpanValue) > VALUE_EPSILON
				? referenceSpanValue
				: null;

	if (spanTime === 0 || effectiveSpanValue === null) {
		return null;
	}

	const rightHandle =
		leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey });
	const leftHandle =
		rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey });

	return [
		clamp01({ value: rightHandle.dt / spanTime }),
		rightHandle.dv / effectiveSpanValue,
		clamp01({ value: 1 + leftHandle.dt / spanTime }),
		1 + leftHandle.dv / effectiveSpanValue,
	];
}

export function getCurveHandlesForNormalizedCubicBezier({
	leftKey,
	rightKey,
	cubicBezier,
	referenceSpanValue,
}: {
	leftKey: ScalarAnimationKey;
	rightKey: ScalarAnimationKey;
	cubicBezier: NormalizedCubicBezier;
	/** Fallback Y-axis scale used when the segment is flat (spanValue ≈ 0). */
	referenceSpanValue?: number;
}): {
	rightHandle: CurveHandle;
	leftHandle: CurveHandle;
} | null {
	const spanTime = rightKey.time - leftKey.time;
	const spanValue = rightKey.value - leftKey.value;
	const effectiveSpanValue =
		Math.abs(spanValue) > VALUE_EPSILON
			? spanValue
			: referenceSpanValue !== undefined && Math.abs(referenceSpanValue) > VALUE_EPSILON
				? referenceSpanValue
				: null;

	if (spanTime === 0 || effectiveSpanValue === null) {
		return null;
	}

	const [rawX1, y1, rawX2, y2] = cubicBezier;
	const x1 = clamp01({ value: rawX1 });
	const x2 = clamp01({ value: rawX2 });

	return {
		rightHandle: {
			dt: spanTime * x1,
			dv: effectiveSpanValue * y1,
		},
		leftHandle: {
			dt: spanTime * (x2 - 1),
			dv: effectiveSpanValue * (y2 - 1),
		},
	};
}
