import * as React from "react";

export interface FileEntry {
	name: string;
	kind: "file" | "dir";
}

export interface ProjectPathMatch {
	name: string;
	path: string;
}

export interface UsePathCompletionArgs {
	pathInput: string;
	setPathInput: (value: string) => void;
	homeDir: string | null;
	listDirectoryAbsolute: (path: string) => Promise<FileEntry[]>;
	debounceMs?: number;
}

export interface UsePathCompletionResult {
	matchList: ProjectPathMatch[];
	selectedMatch: number;
	suggestionTail: string;
	completionError: string | null;
	currentPartial: string;
	setSelectedMatch: (index: number) => void;
	acceptSelectedMatch: () => void;
	acceptMatch: (path: string) => void;
	closeSuggestions: () => void;
}

export function isFuzzyMatch(candidate: string, query: string) {
	if (!query) return true;
	const lowerCandidate = candidate.toLowerCase();
	const lowerQuery = query.toLowerCase();
	let idx = 0;
	for (const char of lowerQuery) {
		idx = lowerCandidate.indexOf(char, idx);
		if (idx === -1) return false;
		idx += 1;
	}
	return true;
}

export function getCurrentPartial(input: string) {
	const trimmed = input.trim();
	if (!trimmed) return "";
	if (trimmed.endsWith("/")) return "";
	const idx = trimmed.lastIndexOf("/");
	return idx >= 0 ? trimmed.slice(idx + 1) : trimmed;
}

export function usePathCompletion({
	pathInput,
	setPathInput,
	homeDir,
	listDirectoryAbsolute,
	debounceMs = 60,
}: UsePathCompletionArgs): UsePathCompletionResult {
	const [matchList, setMatchList] = React.useState<ProjectPathMatch[]>([]);
	const [selectedMatch, setSelectedMatch] = React.useState(0);
	const [suggestionTail, setSuggestionTail] = React.useState("");
	const [completionError, setCompletionError] = React.useState<string | null>(
		null,
	);

	React.useEffect(() => {
		let active = true;

		async function computeSuggestion() {
			setCompletionError(null);
			setSuggestionTail("");
			setMatchList([]);
			setSelectedMatch(0);

			const trimmed = pathInput.trim();
			if (!trimmed) {
				return;
			}

			const endsWithSlash = trimmed.endsWith("/");
			let dir = trimmed;
			let partial = "";

			if (!endsWithSlash) {
				const idx = trimmed.lastIndexOf("/");
				if (idx >= 0) {
					dir = trimmed.slice(0, idx + 1);
					partial = trimmed.slice(idx + 1);
				} else {
					dir = "";
					partial = trimmed;
				}
			}

			if (!dir) {
				if (homeDir) {
					dir = homeDir.endsWith("/") ? homeDir : `${homeDir}/`;
				} else {
					return;
				}
			}

			const dirForListing = dir === "/" ? "/" : dir.replace(/\/+$/, "");
			const entries = await listDirectoryAbsolute(dirForListing);
			if (!active) return;

			const matches = entries
				.filter((entry) => entry.kind === "dir")
				.filter((entry) => isFuzzyMatch(entry.name, partial))
				.sort((a, b) => a.name.localeCompare(b.name))
				.slice(0, 8);

			if (matches.length === 0) {
				return;
			}

			const basePrefix = dir.endsWith("/") ? dir : `${dir}/`;
			const list = matches.map((entry) => ({
				name: entry.name,
				path: `${basePrefix}${entry.name}/`,
			}));
			setMatchList(list);
		}

		const debounceId = window.setTimeout(() => {
			computeSuggestion().catch((error) => {
				console.error(error);
				if (!active) return;
				setCompletionError(
					error instanceof Error
						? error.message
						: "Failed to compute suggestion.",
				);
			});
		}, debounceMs);

		return () => {
			active = false;
			window.clearTimeout(debounceId);
		};
	}, [pathInput, homeDir, listDirectoryAbsolute, debounceMs]);

	React.useEffect(() => {
		if (matchList.length === 0) {
			setSuggestionTail("");
			return;
		}
		const index = Math.min(selectedMatch, matchList.length - 1);
		const next = matchList[index];
		const trimmed = pathInput.trim();
		if (next.path.startsWith(trimmed)) {
			setSuggestionTail(next.path.slice(trimmed.length));
		} else {
			setSuggestionTail("");
		}
	}, [matchList, selectedMatch, pathInput]);

	const acceptMatch = React.useCallback(
		(path: string) => {
			setPathInput(path);
		},
		[setPathInput],
	);

	const acceptSelectedMatch = React.useCallback(() => {
		const next = matchList[selectedMatch]?.path;
		if (next) {
			setPathInput(next);
		}
	}, [matchList, selectedMatch, setPathInput]);

	const closeSuggestions = React.useCallback(() => {
		setMatchList([]);
		setSelectedMatch(0);
		setSuggestionTail("");
		setCompletionError(null);
	}, []);

	return {
		matchList,
		selectedMatch,
		suggestionTail,
		completionError,
		currentPartial: getCurrentPartial(pathInput),
		setSelectedMatch,
		acceptSelectedMatch,
		acceptMatch,
		closeSuggestions,
	};
}
