import { createFileRoute, useNavigate } from "@tanstack/react-router"
import { useCallback, useEffect, useId, useRef } from "react"
import { fetchApi, useAuthenticatedQuery } from "~/api"
import { ActionBar } from "~/app/bookmarks/-action-bar.tsx"
import { useLogOut } from "~/auth.ts"
import { Button } from "~/components/button.tsx"
import { LoadingSpinner } from "~/components/loading-spinner"
import { useMnemonics } from "~/hooks/use-mnemonics.ts"
import { BookmarkList } from "./-bookmark-list"
import { ActionBarContentKind, DialogKind, useBookmarkPageStore } from "./-store"

export const Route = createFileRoute("/bookmarks/")({
	component: RouteComponent,
})

function RouteComponent() {
	return (
		<main className="w-full flex justify-center">
			<BookmarkListPane />
			<BookmarkListActionBar className="fixed left-0 right-0 bottom-0" />
		</main>
	)
}

function BookmarkListPane() {
	return (
		<div className="flex flex-col py-16 container max-w-3xl md:flex-row lg:py-32">
			<header className="mb-4 md:mb-0 md:mr-16 text-start">
				<h1 className="font-bold text-start">
					<span className="invisible md:hidden">&nbsp;&gt;&nbsp;</span>
					YOUR BOOKMARKS
				</h1>
			</header>
			<div className="flex-1">
				<BookmarkListContainer />
			</div>
		</div>
	)
}

function BookmarkListContainer() {
	const searchParamsString = new URLSearchParams(Route.useSearch()).toString()
	const { data: bookmarks, status } = useAuthenticatedQuery(
		searchParamsString ? ["bookmarks", searchParamsString] : ["bookmarks"],
		async () => {
			const res = await fetchApi(searchParamsString ? `/bookmarks?${searchParamsString}` : "/bookmarks")
			return await res.json()
		},
	)
	const handleBookmarkListItemAction = useBookmarkPageStore((state) => state.handleBookmarkListItemAction)

	switch (status) {
		case "success":
			return (
				<BookmarkList
					className="-mt-2"
					alwaysExpandItem={false}
					bookmarks={bookmarks}
					onItemAction={handleBookmarkListItemAction}
				/>
			)

		case "pending":
			return (
				<p>
					Loading <LoadingSpinner />
				</p>
			)

		case "error":
			return <p>error loading bookmarks</p>
	}
}

function BookmarkListActionBar({ className }: { className?: string }) {
	const content = useBookmarkPageStore((state) => state.actionBarContent)
	return (
		<ActionBar className={className}>
			{(() => {
				switch (content.kind) {
					case ActionBarContentKind.Normal:
						return <ActionButtons />
					case ActionBarContentKind.StatusMessage:
						return <p>{content.message}</p>
					case ActionBarContentKind.SearchBar:
						return <SearchBar />
				}
			})()}
		</ActionBar>
	)
}

function SearchBar() {
	const searchInputId = useId()
	const inputRef = useRef<HTMLInputElement | null>(null)
	const setActionBarContent = useBookmarkPageStore((state) => state.setActionBarContent)
	const searchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
	const navigate = Route.useNavigate()
	const { q } = Route.useSearch()

	useMnemonics(
		{
			Escape: () => {
				navigate({
					search: (prevSearch: Record<string, string | undefined>) => ({
						...prevSearch,
						tags: undefined,
						q: undefined,
					}),
				})
				setActionBarContent({ kind: ActionBarContentKind.Normal })
			},
		},
		{ ignore: () => false },
	)

	useEffect(() => {
		setInterval(() => {
			inputRef.current?.focus()
		}, 0)
	}, [])

	useEffect(
		() => () => {
			if (searchTimeoutRef.current) {
				clearTimeout(searchTimeoutRef.current)
			}
		},
		[],
	)

	function pushSearchQuery(event: React.ChangeEvent<HTMLInputElement>) {
		if (searchTimeoutRef.current) {
			clearTimeout(searchTimeoutRef.current)
		}
		const q = event.currentTarget.value
		searchTimeoutRef.current = setTimeout(() => {
			navigate({
				search: (prevSearch: Record<string, string | undefined>) => {
					if (!q) {
						return { ...prevSearch, tags: undefined, q: undefined }
					}

					if (q.startsWith("#")) {
						const parts = q.split(" ")
						let searchTermBegin = -1
						for (let i = 0; i < parts.length; ++i) {
							if (!parts[i].startsWith("#")) {
								searchTermBegin = i
								break
							}
						}
						const tags = (searchTermBegin >= 0 ? parts.slice(0, searchTermBegin) : parts)
							.map((tag) => tag.substring(1))
							.join(",")
						const query = searchTermBegin >= 0 ? parts.slice(searchTermBegin).join(" ") : ""

						if (query) {
							return { ...prevSearch, tags, q: query }
						}

						return { ...prevSearch, tags, q: undefined }
					}

					return { ...prevSearch, tags: undefined, q }
				},
			})
		}, 500)
	}

	function closeSearchBar() {
		setActionBarContent({ kind: ActionBarContentKind.Normal })
	}

	return (
		<div className="-my-4 w-full flex items-center justify-center">
			<label className="p-4 bg-stone-900 dark:bg-stone-200 text-stone-300 dark:text-stone-800" htmlFor={searchInputId}>
				SEARCH
			</label>
			<input
				ref={inputRef}
				id={searchInputId}
				type="text"
				className="flex-1 text-center outline-0 ring-0"
				defaultValue={q ?? ""}
				onChange={pushSearchQuery}
			/>
			<Button className="mx-2" onClick={closeSearchBar}>
				CLOSE
			</Button>
		</div>
	)
}

function ActionButtons() {
	const setActiveDialog = useBookmarkPageStore((state) => state.setActiveDialog)
	const setActionBarContent = useBookmarkPageStore((state) => state.setActionBarContent)

	useMnemonics(
		{
			a: addBookmark,
			s: openSearchBar,
		},
		{ ignore: useCallback(() => useBookmarkPageStore.getState().dialog.kind !== DialogKind.None, []) },
	)

	function addBookmark() {
		setActiveDialog({ kind: DialogKind.AddBookmark })
	}

	function openSearchBar() {
		setActionBarContent({ kind: ActionBarContentKind.SearchBar })
	}

	return (
		<div className="flex flex-row justify-center space-x-4">
			<Button onClick={addBookmark}>
				<span className="underline">A</span>DD
			</Button>
			<Button onClick={openSearchBar}>
				<span className="underline">S</span>EARCH
			</Button>
			<LogOutButton />
		</div>
	)
}
function LogOutButton() {
	const logOutMutation = useLogOut()
	const navigate = useNavigate()

	function logOut() {
		logOutMutation.mutate()
		navigate({ to: "/", replace: true })
	}

	return (
		<Button disabled={logOutMutation.isPending} onClick={logOut}>
			LOG OUT
		</Button>
	)
}

export { BookmarkListActionBar }
