import React, { useEffect, useState, useRef } from "react";
import * as THREE from "three";
import { useFrame, useThree } from "@react-three/fiber";
import { NavMeshQuery } from "@recast-navigation/core";
import { Line } from "@react-three/drei";
import { usePlayButtonStore } from "../../../store/usePlayButtonStore";

interface PathNavigatorProps {
	navMesh: any;
	pathPoints: any;
	id: string;
	speed: number;
	bufferTime: number;
	hitCount: number;
}

export default function PathNavigator({
	navMesh,
	pathPoints,
	id,
	speed,
	bufferTime,
	hitCount
}: PathNavigatorProps) {
	const [path, setPath] = useState<[number, number, number][]>([]);
	const [currentPhase, setCurrentPhase] = useState<'initial' | 'loop'>('initial');
	const [toPickupPath, setToPickupPath] = useState<[number, number, number][]>([]);
	const [pickupDropPath, setPickupDropPath] = useState<[number, number, number][]>([]);
	const [dropPickupPath, setDropPickupPath] = useState<[number, number, number][]>([]);
	const [initialPosition, setInitialPosition] = useState<THREE.Vector3 | null>(null);
	const [initialRotation, setInitialRotation] = useState<THREE.Euler | null>(null);

	const distancesRef = useRef<number[]>([]);
	const totalDistanceRef = useRef(0);
	const progressRef = useRef(0);
	const isWaiting = useRef(false);
	const timeoutRef = useRef<NodeJS.Timeout | null>(null);

	const { scene } = useThree();
	const { isPlaying } = usePlayButtonStore();

	useEffect(() => {
		const object = scene.getObjectByProperty("uuid", id);
		if (object) {
			setInitialPosition(object.position.clone());
			setInitialRotation(object.rotation.clone());
		}
	}, [scene, id]);

	const computePath = (start: any, end: any) => {
		try {
			const navMeshQuery = new NavMeshQuery(navMesh);
			const { path: segmentPath } = navMeshQuery.computePath(start, end);
			return segmentPath?.map(({ x, y, z }) => [x, y + 0.1, z] as [number, number, number]) || [];
		} catch {
			return [];
		}
	};

	const resetState = () => {
		if (timeoutRef.current) {
			clearTimeout(timeoutRef.current);
			timeoutRef.current = null;
		}

		setPath([]);
		setCurrentPhase('initial');
		setPickupDropPath([]);
		setDropPickupPath([]);
		distancesRef.current = [];
		totalDistanceRef.current = 0;
		progressRef.current = 0;
		isWaiting.current = false;

		if (initialPosition && initialRotation) {
			const object = scene.getObjectByProperty("uuid", id);
			if (object) {
				object.position.copy(initialPosition);
				object.rotation.copy(initialRotation);
			}
		}
	};

	useEffect(() => {
		if (!isPlaying) {
			resetState();
		}

		if (!navMesh || pathPoints.length < 2) return;

		const [pickup, drop] = pathPoints.slice(-2);
		const object = scene.getObjectByProperty("uuid", id);
		if (!object) return;

		const currentPosition = { x: object.position.x, y: object.position.y, z: object.position.z };

		const toPickupPath = computePath(currentPosition, pickup);
		const pickupToDropPath = computePath(pickup, drop);
		const dropToPickupPath = computePath(drop, pickup);

		if (toPickupPath.length && pickupToDropPath.length && dropToPickupPath.length) {
			setPickupDropPath(pickupToDropPath);
			setDropPickupPath(dropToPickupPath);
			setToPickupPath(toPickupPath);
			setPath(toPickupPath);
			setCurrentPhase('initial');
		}
	}, [navMesh, pathPoints, hitCount, isPlaying]);

	useEffect(() => {
		if (path.length < 2) return;

		let total = 0;
		const segmentDistances = path.slice(0, -1).map((point, i) => {
			const dist = new THREE.Vector3(...point).distanceTo(new THREE.Vector3(...path[i + 1]));
			total += dist;
			return dist;
		});

		distancesRef.current = segmentDistances;
		totalDistanceRef.current = total;
		progressRef.current = 0;
		isWaiting.current = false;
	}, [path]);

	useFrame((_, delta) => {
		if (!isPlaying || path.length < 2 || !scene || !id) return;

		const object = scene.getObjectByProperty("uuid", id);
		if (!object) return;

		const speedFactor = speed;
		progressRef.current += delta * speedFactor;

		let covered = progressRef.current;
		let accumulated = 0;
		let index = 0;

		while (
			index < distancesRef.current.length &&
			covered > accumulated + distancesRef.current[index]
		) {
			accumulated += distancesRef.current[index];
			index++;
		}

		if (index >= distancesRef.current.length) {
			progressRef.current = totalDistanceRef.current;

			if (!isWaiting.current) {
				isWaiting.current = true;

				timeoutRef.current = setTimeout(() => {
					if (currentPhase === 'initial') {
						setPath(pickupDropPath);
						setCurrentPhase('loop');
					} else {
						setPath(prevPath =>
							prevPath === pickupDropPath ? dropPickupPath : pickupDropPath
						);
					}

					progressRef.current = 0;
					isWaiting.current = false;
				}, bufferTime * 1000);
			}
			return;
		}

		const start = new THREE.Vector3(...path[index]);
		const end = new THREE.Vector3(...path[index + 1]);
		const dist = distancesRef.current[index];
		const t = THREE.MathUtils.clamp((covered - accumulated) / dist, 0, 1);
		const position = start.clone().lerp(end, t);

		object.position.copy(position);

		const direction = new THREE.Vector3().subVectors(end, start).normalize();
		const targetRotationY = Math.atan2(direction.x, direction.z);

		let angleDifference = targetRotationY - object.rotation.y;
		angleDifference = ((angleDifference + Math.PI) % (Math.PI * 2)) - Math.PI;
		object.rotation.y += angleDifference * 0.1;
	});

	useEffect(() => {
		return () => {
			if (timeoutRef.current) {
				clearTimeout(timeoutRef.current);
			}
		};
	}, []);

	return (
		<group name="path-navigator-lines" visible={!isPlaying} >
			{toPickupPath.length > 0 && (
				<Line points={toPickupPath} color="blue" lineWidth={3} dashed dashSize={0.75} dashScale={2} />
			)}

			{pickupDropPath.length > 0 && (
				<Line points={pickupDropPath} color="red" lineWidth={3} />
			)}

			{dropPickupPath.length > 0 && (
				<Line points={dropPickupPath} color="red" lineWidth={3} />
			)}
		</group>
	);
}