// @ts-nocheck

const DEFAULT_FRAME_WIDTH = 160;
const DEFAULT_FRAME_HEIGHT = 90;
const PIXEL_DELTA_THRESHOLD = 18;
const DEFAULT_SMOOTHING_FACTOR = 0.35;

export const clampScore = (value) => {
	if (!Number.isFinite(value)) return 0;
	return Math.min(1, Math.max(0, value));
};

export const computeLuma = (imageData) => {
	const luminance = new Float32Array(imageData.data.length / 4);
	for (let offset = 0, index = 0; offset < imageData.data.length; offset += 4, index += 1) {
		luminance[index] =
			imageData.data[offset] * 0.299 + imageData.data[offset + 1] * 0.587 + imageData.data[offset + 2] * 0.114;
	}
	return luminance;
};

export const computeChangedPixelRatio = (previousFrame, currentFrame, pixelDeltaThreshold = PIXEL_DELTA_THRESHOLD) => {
	if (!previousFrame || !currentFrame || previousFrame.length !== currentFrame.length || currentFrame.length === 0) {
		return 0;
	}

	let changedPixels = 0;
	for (let index = 0; index < currentFrame.length; index += 1) {
		if (Math.abs(currentFrame[index] - previousFrame[index]) >= pixelDeltaThreshold) {
			changedPixels += 1;
		}
	}

	return clampScore(changedPixels / currentFrame.length);
};

export const createMotionMachineSnapshot = (overrides = {}) => ({
	consecutiveHighFrames: 0,
	activeMotion: false,
	quietSince: null,
	activeSince: null,
	...overrides
});

export const applyMotionStateMachine = (snapshot, analysis, config = {}, now = Date.now()) => {
	const nextSnapshot = createMotionMachineSnapshot(snapshot);
	const score = clampScore(analysis?.score);
	const analysisState = analysis?.state ?? 'monitoring';
	const triggerThreshold = config.triggerThreshold ?? 0.12;
	const releaseThreshold = config.releaseThreshold ?? 0.04;
	const consecutiveRequired = config.consecutiveTriggerFrames ?? 3;
	const minimumEventMs = config.minimumEventMs ?? 8000;
	const cooldownMs = config.cooldownMs ?? 9000;

	if (analysisState === 'warming_up') {
		nextSnapshot.consecutiveHighFrames = 0;
		if (!nextSnapshot.activeMotion) {
			nextSnapshot.quietSince = null;
			return {
				snapshot: nextSnapshot,
				output: {
					state: 'warming_up',
					score,
					activeMotion: nextSnapshot.activeMotion,
					activeSince: nextSnapshot.activeSince,
					quietSince: nextSnapshot.quietSince
				}
			};
		}
	}

	if (!nextSnapshot.activeMotion) {
		if (score >= triggerThreshold) {
			nextSnapshot.consecutiveHighFrames += 1;
			if (nextSnapshot.consecutiveHighFrames >= consecutiveRequired) {
				nextSnapshot.activeMotion = true;
				nextSnapshot.activeSince = now;
				nextSnapshot.quietSince = null;
				return {
					snapshot: nextSnapshot,
					output: {
						state: 'triggered',
						score,
						activeMotion: nextSnapshot.activeMotion,
						activeSince: nextSnapshot.activeSince,
						quietSince: nextSnapshot.quietSince
					}
				};
			}

			return {
				snapshot: nextSnapshot,
				output: {
					state: 'candidate_motion',
					score,
					activeMotion: nextSnapshot.activeMotion,
					activeSince: nextSnapshot.activeSince,
					quietSince: nextSnapshot.quietSince
				}
			};
		}

		nextSnapshot.consecutiveHighFrames = 0;
		return {
			snapshot: nextSnapshot,
			output: {
				state: analysisState === 'warming_up' ? 'warming_up' : 'monitoring',
				score,
				activeMotion: nextSnapshot.activeMotion,
				activeSince: nextSnapshot.activeSince,
				quietSince: nextSnapshot.quietSince
			}
		};
	}

	if (score >= releaseThreshold) {
		nextSnapshot.quietSince = null;
		return {
			snapshot: nextSnapshot,
			output: {
				state: 'triggered',
				score,
				activeMotion: nextSnapshot.activeMotion,
				activeSince: nextSnapshot.activeSince,
				quietSince: nextSnapshot.quietSince
			}
		};
	}

	if (!nextSnapshot.quietSince) {
		nextSnapshot.quietSince = now;
	}

	if (now - (nextSnapshot.activeSince ?? now) < minimumEventMs) {
		return {
			snapshot: nextSnapshot,
			output: {
				state: 'triggered',
				score,
				activeMotion: nextSnapshot.activeMotion,
				activeSince: nextSnapshot.activeSince,
				quietSince: nextSnapshot.quietSince
			}
		};
	}

	if (now - nextSnapshot.quietSince >= cooldownMs) {
		nextSnapshot.activeMotion = false;
		nextSnapshot.activeSince = null;
		nextSnapshot.quietSince = null;
		nextSnapshot.consecutiveHighFrames = 0;
		return {
			snapshot: nextSnapshot,
			output: {
				state: 'cooldown',
				score,
				activeMotion: nextSnapshot.activeMotion,
				activeSince: nextSnapshot.activeSince,
				quietSince: nextSnapshot.quietSince
			}
		};
	}

	return {
		snapshot: nextSnapshot,
		output: {
			state: 'cooldown',
			score,
			activeMotion: nextSnapshot.activeMotion,
			activeSince: nextSnapshot.activeSince,
			quietSince: nextSnapshot.quietSince
		}
	};
};

export const createMotionDetector = ({
	getSourceElement,
	getConfig,
	onUpdate,
	frameWidth = DEFAULT_FRAME_WIDTH,
	frameHeight = DEFAULT_FRAME_HEIGHT,
	smoothingFactor = DEFAULT_SMOOTHING_FACTOR
} = {}) => {
	const canvas = document.createElement('canvas');
	canvas.width = frameWidth;
	canvas.height = frameHeight;
	const context = canvas.getContext('2d', { willReadFrequently: true });

	let active = false;
	let timerId = null;
	let previousFrame = null;
	let smoothedScore = 0;
	let machineSnapshot = createMotionMachineSnapshot();
	let currentState = 'idle';

	const emitUpdate = (updates = {}) => {
		const score = clampScore(updates.score ?? smoothedScore);
		currentState = updates.state ?? currentState;
		onUpdate?.({
			state: currentState,
			score,
			activeMotion: machineSnapshot.activeMotion,
			activeSince: machineSnapshot.activeSince,
			quietSince: machineSnapshot.quietSince
		});
	};

	const clearTimer = () => {
		if (timerId) {
			clearTimeout(timerId);
			timerId = null;
		}
	};

	const reset = (state = 'idle') => {
		clearTimer();
		previousFrame = null;
		smoothedScore = 0;
		machineSnapshot = createMotionMachineSnapshot();
		emitUpdate({ state, score: 0 });
	};

	const scheduleNextTick = (ms) => {
		clearTimer();
		timerId = setTimeout(() => {
			void tick();
		}, Math.max(60, ms));
	};

	const analyzeFrame = () => {
		if (!context) {
			return { state: 'error', score: 0 };
		}

		const sourceElement = getSourceElement?.();
		if (
			!sourceElement ||
			sourceElement.readyState < 2 ||
			!sourceElement.videoWidth ||
			!sourceElement.videoHeight
		) {
			return { state: 'warming_up', score: 0 };
		}

		context.drawImage(sourceElement, 0, 0, frameWidth, frameHeight);
		const imageData = context.getImageData(0, 0, frameWidth, frameHeight);
		const currentFrame = computeLuma(imageData);

		if (!previousFrame) {
			previousFrame = currentFrame;
			return { state: 'warming_up', score: 0 };
		}

		const rawScore = computeChangedPixelRatio(previousFrame, currentFrame, PIXEL_DELTA_THRESHOLD);
		previousFrame = currentFrame;
		smoothedScore = clampScore(smoothedScore * (1 - smoothingFactor) + rawScore * smoothingFactor);

		return {
			state: 'monitoring',
			score: smoothedScore
		};
	};

	const tick = async () => {
		if (!active) return;

		const config = getConfig?.() ?? {};
		const analysis = analyzeFrame();
		const next = applyMotionStateMachine(machineSnapshot, analysis, config, Date.now());
		machineSnapshot = next.snapshot;
		emitUpdate(next.output);

		const activeInterval =
			next.output.state === 'candidate_motion' ||
			next.output.state === 'triggered' ||
			next.output.state === 'cooldown';
		scheduleNextTick(activeInterval ? config.burstIntervalMs ?? 300 : config.sampleIntervalMs ?? 1000);
	};

	return {
		start() {
			if (active) return;
			active = true;
			reset('warming_up');
			scheduleNextTick(0);
		},
		stop(reason = 'idle') {
			active = false;
			reset(reason);
		},
		isRunning() {
			return active;
		},
		getSnapshot() {
			return {
				active,
				state: currentState,
				score: clampScore(smoothedScore),
				activeMotion: machineSnapshot.activeMotion
			};
		}
	};
};
