"use client";

import React from "react";
import { search as apiSearch } from "@/lib/frontend/apiClient";
import { buildSearchApiInput } from "@/lib/frontend/search/searchApiInput";

/**
 * useSearchQuery
 *
 * Purpose:
 * - Encapsulate the Search UI data lifecycle in one place:
 *   - "first page" load (URL-driven, cursor reset)
 *   - "load more" pagination (cursor-based, append)
 *   - race protection (ignore stale responses)
 *
 * Design rules:
 * - The URL (searchKey) is the identity of the first page.
 * - Cursor is intentionally NOT part of searchKey (not shareable).
 * - When searchKey changes, we reset and load the first page again.
 *
 * Returned state is UI-friendly and stable:
 * - status: "idle" | "loading" | "success" | "error"
 * - items / nextCursor / total / error
 * - loadMore() / isLoadingMore / loadMoreError
 * - retry() triggers a re-fetch of the first page for the current searchKey
 *
 * NOTE:
 * - This hook does not do any redirects or UI mapping.
 * - Mapping to German user-facing messages stays in SearchPage/SearchResults via mapSearchError().
 *
 * @param {{
 *   searchKey: string,
 *   urlState: {
 *     q: string|null,
 *     scope: "single"|"multi"|"all",
 *     branch: string|null,
 *     branches: string[],
 *     limit: number,
 *     from: string|null,
 *     to: string|null
 *   },
 *   routeBranch: string,
 *   user: { role: string, branchId: string|null }|null,
 *   limit?: number
 * }} args
 */
export function useSearchQuery({
	searchKey,
	urlState,
	routeBranch,
	user,
	limit = 100,
}) {
	const [status, setStatus] = React.useState("idle"); // idle|loading|success|error
	const [items, setItems] = React.useState([]);
	const [nextCursor, setNextCursor] = React.useState(null);
	const [total, setTotal] = React.useState(null); // number|null
	const [error, setError] = React.useState(null);

	const [isLoadingMore, setIsLoadingMore] = React.useState(false);
	const [loadMoreError, setLoadMoreError] = React.useState(null);

	// Retry tick triggers a first-page reload without changing searchKey.
	const [retryTick, setRetryTick] = React.useState(0);

	// Track the latest searchKey so "load more" cannot append into a newer search.
	const searchKeyRef = React.useRef(searchKey);
	React.useEffect(() => {
		searchKeyRef.current = searchKey;
	}, [searchKey]);

	// Race protection:
	// - firstPageRequestId increments for each first-page fetch
	// - loadMoreRequestId increments for each loadMore fetch
	const firstPageRequestIdRef = React.useRef(0);
	const loadMoreRequestIdRef = React.useRef(0);

	const retry = React.useCallback(() => {
		setRetryTick((n) => n + 1);
	}, []);

	const runFirstPage = React.useCallback(async () => {
		// Always reset "load more" state when starting a new first-page request.
		setIsLoadingMore(false);
		setLoadMoreError(null);

		const { input, error: localError } = buildSearchApiInput({
			urlState,
			routeBranch,
			user,
			cursor: null,
			limit,
		});

		// No query => we are in "idle" mode (no results, no error).
		if (!input && !localError) {
			setStatus("idle");
			setItems([]);
			setNextCursor(null);
			setTotal(null);
			setError(null);
			return;
		}

		// Local validation failed (e.g. multi without branches) => show error without calling API.
		if (localError) {
			setStatus("error");
			setItems([]);
			setNextCursor(null);
			setTotal(null);
			setError(localError);
			return;
		}

		const requestId = ++firstPageRequestIdRef.current;

		setStatus("loading");
		setItems([]);
		setNextCursor(null);
		setTotal(null);
		setError(null);

		try {
			const res = await apiSearch(input);

			// Ignore stale responses.
			if (requestId !== firstPageRequestIdRef.current) return;

			const nextItems = Array.isArray(res?.items) ? res.items : [];
			const next =
				typeof res?.nextCursor === "string" && res.nextCursor.trim()
					? res.nextCursor
					: null;

			const t =
				typeof res?.total === "number" && Number.isFinite(res.total)
					? res.total
					: null;

			setItems(nextItems);
			setNextCursor(next);
			setTotal(t);
			setStatus("success");
		} catch (err) {
			if (requestId !== firstPageRequestIdRef.current) return;

			setItems([]);
			setNextCursor(null);
			setTotal(null);
			setError(err);
			setStatus("error");
		}
	}, [urlState, routeBranch, user, limit]);

	React.useEffect(() => {
		runFirstPage();
	}, [runFirstPage, searchKey, retryTick]);

	const loadMore = React.useCallback(async () => {
		if (!nextCursor) return;
		if (isLoadingMore) return;

		const baseKey = searchKeyRef.current;

		const { input, error: localError } = buildSearchApiInput({
			urlState,
			routeBranch,
			user,
			cursor: nextCursor,
			limit,
		});

		if (localError) {
			setLoadMoreError(localError);
			return;
		}
		if (!input) return;

		const requestId = ++loadMoreRequestIdRef.current;

		setIsLoadingMore(true);
		setLoadMoreError(null);

		try {
			const res = await apiSearch(input);

			// Ignore stale responses.
			if (requestId !== loadMoreRequestIdRef.current) return;

			// If the base search changed since the click, do not append.
			if (searchKeyRef.current !== baseKey) return;

			const moreItems = Array.isArray(res?.items) ? res.items : [];
			const next =
				typeof res?.nextCursor === "string" && res.nextCursor.trim()
					? res.nextCursor
					: null;

			const t =
				typeof res?.total === "number" && Number.isFinite(res.total)
					? res.total
					: null;

			setItems((prev) => [...prev, ...moreItems]);
			setNextCursor(next);

			// Keep total stable but refresh when the backend provides it.
			if (t !== null) setTotal(t);
		} catch (err) {
			if (requestId !== loadMoreRequestIdRef.current) return;
			setLoadMoreError(err);
		} finally {
			// Only the latest loadMore call can clear the loading flag.
			if (requestId === loadMoreRequestIdRef.current) {
				setIsLoadingMore(false);
			}
		}
	}, [nextCursor, isLoadingMore, urlState, routeBranch, user, limit]);

	return {
		status,
		items,
		nextCursor,
		total,
		error,
		retry,
		loadMore,
		isLoadingMore,
		loadMoreError,
	};
}
