import clsx from 'clsx';
import { Bell, BookMarked, LogOut, Search, Settings, UserRound, Plus, LineSquiggle, BookPlus } from 'lucide-solid';
import { A, useLocation, useNavigate } from '@solidjs/router';
import { createQuery } from '@tanstack/solid-query';
import { For, Show, createEffect, createSignal, onCleanup, onMount, type Component, type JSX } from 'solid-js';

import { Avatar, buttonStyles, inputStyles } from './components/common';
import { resolveActor } from './lib/api/identity';
import { useAuth } from './lib/auth';
import { useLiveEvents, type LiveEvent } from './lib/live-events';
import { formatRelativeTime } from './lib/repo-utils';

const TOPBAR_MENU_SELECTOR = 'details[data-topbar-menu]';

const closeOpenTopbarMenus = (root: HTMLElement, target: Node) => {
	root.querySelectorAll<HTMLDetailsElement>(TOPBAR_MENU_SELECTOR).forEach((menu) => {
		if (menu.open && !menu.contains(target)) {
			menu.open = false;
		}
	});
};

const eventLabel = (event: LiveEvent) => {
	if (event.source === 'sh.tangled.repo.issue:repo') {
		return `new issue on ${event.subject}`;
	}

	if (event.source === 'sh.tangled.repo.pull:target.repo') {
		return `new pull on ${event.subject}`;
	}

	return `${event.source} -> ${event.subject}`;
};

const LoginMenu: Component = () => {
	const auth = useAuth();
	const [identifier, setIdentifier] = createSignal('');

	const onSignIn = async (event: SubmitEvent) => {
		event.preventDefault();
		await auth.signIn(identifier());
	};

	return (
		<details data-topbar-menu class="relative">
			<summary class={clsx(buttonStyles(), 'cursor-pointer list-none')}>login</summary>
			<div class="absolute right-0 mt-3 w-80 rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 p-4 shadow-lg z-50">
				<form onSubmit={onSignIn} class="flex flex-col gap-3">
					<input
						value={identifier()}
						onInput={(event) => setIdentifier(event.currentTarget.value)}
						placeholder="handle or did"
						class={inputStyles()}
					/>
					<button type="submit" class={clsx(buttonStyles('primary'), 'justify-center')}>
						continue with OAuth
					</button>
					<Show when={auth.error()}>
						<p class="text-sm text-red-500">{auth.error()}</p>
					</Show>
				</form>
			</div>
		</details>
	);
};

const NotificationsMenu: Component = () => {
	const live = useLiveEvents();

	return (
		<details data-topbar-menu class="relative">
			<summary
				class="cursor-pointer list-none flex items-center gap-2"
				onClick={() => live.markRead()}
			>
				<div class="relative">
					<Bell class="size-5 text-gray-500 dark:text-gray-400" />
					<Show when={live.unread() > 0}>
						<span class="untangled-unread-badge absolute -top-2 -right-2 min-w-5 h-5 rounded-full bg-blue-600 text-white flex items-center justify-center px-1">
							{Math.min(live.unread(), 99)}
						</span>
					</Show>
				</div>
			</summary>
			<div class="absolute right-0 mt-3 w-80 rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-lg z-50 overflow-hidden">
				<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 font-medium">live activity</div>
				<div class="max-h-96 overflow-auto">
					<Show
						when={live.events().length > 0}
						fallback={<div class="px-4 py-6 text-sm text-gray-500 dark:text-gray-400">No live events yet.</div>}
					>
						<For each={live.events()}>
							{(event) => (
								<div class="px-4 py-3 border-b border-gray-200 dark:border-gray-700 text-sm">
									<div class="font-medium break-all">{eventLabel(event)}</div>
									<div class="text-gray-500 dark:text-gray-400">{formatRelativeTime(event.receivedAt)}</div>
								</div>
							)}
						</For>
					</Show>
				</div>
			</div>
		</details>
	);
};

const UserProfileMenu: Component = () => {
	const auth = useAuth();

	const viewerQuery = createQuery(() => ({
		queryKey: ['viewer', auth.currentDid()],
		enabled: Boolean(auth.currentDid()),
		queryFn: async () => resolveActor(auth.currentDid()!),
	}));

	const closeMenu = (e: MouseEvent) => {
		const details = (e.currentTarget as HTMLElement).closest('details');
		if (details) details.open = false;
	};

	return (
		<details data-topbar-menu class="untangled-topbar-menu">
			<summary class="untangled-topbar-menu-summary">
				<Avatar did={auth.currentDid()!} size="size-8 md:size-6" />
				<span class="hidden md:inline">{viewerQuery.data?.handle ?? auth.currentDid()}</span>
			</summary>
			<div class="untangled-topbar-menu-dropdown">
				{/* <button type="button" class="untangled-topbar-menu-item" onClick={() => {
					const id = prompt('Enter handle or DID');
					if (id) void auth.signIn(id);
				}}>
					<div class="untangled-topbar-menu-add-account-icon">
						<Plus class="size-4" />
					</div>
					add account
				</button> */}

				<div class="untangled-topbar-menu-item-separator">
					<Show when={viewerQuery.data?.handle}>
						{(handle) => (
							<>
								<A href={`/${handle()}`} class="untangled-topbar-menu-item" onClick={closeMenu}>
									<UserRound class="size-4" />
									profile
								</A>
								<A href={`/${handle()}?tab=repos`} class="untangled-topbar-menu-item" onClick={closeMenu}>
									<BookMarked class="size-4" />
									repositories
								</A>
								<A href={`/${handle()}?tab=strings`} class="untangled-topbar-menu-item" onClick={closeMenu}>
									<LineSquiggle class="size-4" />
									strings
								</A>
							</>
						)}
					</Show>

					<A href="/settings" class="untangled-topbar-menu-item" onClick={closeMenu}>
						<Settings class="size-4" />
						settings
					</A>

					<button
						type="button"
						class="untangled-topbar-menu-item untangled-topbar-menu-item-danger"
						onClick={() => void auth.signOut()}
					>
						<LogOut class="size-4" />
						logout
					</button>
				</div>
			</div>
		</details>
	);
};

const NewMenu: Component = () => {
	const closeMenu = (e: MouseEvent) => {
		const details = (e.currentTarget as HTMLElement).closest('details');
		if (details) details.open = false;
	};

	return (
		<details data-topbar-menu class="untangled-topbar-menu">
			<summary class="untangled-topbar-menu-summary btn text-sm no-underline hover:no-underline flex items-center gap-2 group border border-gray-200 dark:border-gray-700 rounded-sm px-3 py-1.5 bg-white dark:bg-gray-800 cursor-pointer">
				<Plus class="size-4" />
				<span class="hidden md:inline">new</span>
			</summary>
			<div class="untangled-topbar-menu-dropdown">
				<div class="untangled-topbar-menu-item-separator">
					<A href="/repo/new" class="untangled-topbar-menu-item" onClick={closeMenu}>
						<BookPlus class="size-4" />
						new repository
					</A>
					<A href="/strings/new" class="untangled-topbar-menu-item" onClick={closeMenu}>
						<LineSquiggle class="size-4" />
						new string
					</A>
				</div>
			</div>
		</details>
	);
};

const TopbarSearch: Component = () => {
	const navigate = useNavigate();
	const location = useLocation();
	const [query, setQuery] = createSignal('');

	let inputRef: HTMLInputElement | undefined;
	onMount(() => {
	  const handler = (e: KeyboardEvent) => {
	  	if (e.ctrlKey || e.metaKey) return;
	    if (e.key === "/") {
	      const active = document.activeElement;
	      if (active && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || (active as HTMLElement).isContentEditable)) {
	        return;
	      }

	      e.preventDefault();
	      inputRef?.focus();
	    } else if (e.key === "Escape") {
	    	setQuery('');
	    	inputRef?.blur();
	    }
	  }
	  window.addEventListener("keydown", handler)
	  onCleanup(() => window.removeEventListener("keydown", handler))
	});

	createEffect(() => {
		if (location.pathname !== '/search') {
			setQuery('');
			return;
		}

		const params = new URLSearchParams(location.search);
		setQuery(params.get('q') ?? '');
	});

	const onSubmit = (event: SubmitEvent) => {
		event.preventDefault();
		const trimmed = query().trim();
		navigate(trimmed ? `/search?q=${encodeURIComponent(trimmed)}` : '/search');
	};

	return (
		<form onSubmit={onSubmit} class="relative hidden md:block">
			<div class="untangled-topbar-search-box">
				<Search class="size-4 shrink-0 text-gray-400" />
				<input
					ref={inputRef}
					value={query()}
					onInput={(event) => setQuery(event.currentTarget.value)}
					placeholder="search..."
					class="untangled-topbar-search-input"
				/>
				<kbd class="untangled-topbar-search-kbd">/</kbd>
			</div>
		</form>
	);
};

const Topbar: Component = () => {
	const auth = useAuth();
	let topbar: HTMLElement | undefined;

	onMount(() => {
		const closeMenusForOutsideClick = (event: PointerEvent) => {
			const target = event.target;
			if (!topbar || !(target instanceof Node)) {
				return;
			}

			closeOpenTopbarMenus(topbar, target);
		};

		document.addEventListener('pointerdown', closeMenusForOutsideClick);

		onCleanup(() => {
			document.removeEventListener('pointerdown', closeMenusForOutsideClick);
		});
	});

	return (
		<nav ref={(element) => { topbar = element; }} class="mx-auto space-x-4 px-4 sm:px-6 py-2">
			<div class="flex justify-between gap-4 p-0 items-center">
				<div class="flex shrink-0 items-center gap-2">
					<A href="/" class="flex shrink-0 items-center gap-2 no-underline hover:no-underline">
						<img src="/static/logos/dolly.svg" alt="" class="size-8" />
						<div class="flex flex-col">
							<span class="font-bold text-xl leading-none">untangled</span>
							<a
								href="https://untangled.wisp.place/ptr.pet/untangled"
								class="text-[10px] leading-none text-gray-400 dark:text-gray-500 hover:text-blue-500 dark:hover:text-blue-400 no-underline transition-colors mt-0.5"
								onClick={(e) => e.stopPropagation()}
							>
								src
							</a>
						</div>
					</A>
				</div>

				<div class="flex shrink-0 items-center gap-4">
					<TopbarSearch />
					<A href="/search" class="md:hidden text-gray-500 dark:text-gray-400" title="Search">
						<Search class="size-5" />
					</A>
					<Show
						when={auth.currentDid()}
						fallback={<LoginMenu />}
					>
						<NewMenu />
						<NotificationsMenu />
						<UserProfileMenu />
					</Show>
				</div>
			</div>
		</nav>
	);
};

export const RootShell: Component<{ children?: JSX.Element }> = (props) => {
	const location = useLocation();
	const isRaw = () => location.pathname.endsWith('/raw');

	return (
		<Show when={!isRaw()} fallback={<>{props.children}</>}>
			<div class="min-h-screen flex flex-col gap-4 bg-slate-100 dark:bg-gray-900 dark:text-white">
				<header class="untangled-safe-header w-full shadow-sm dark:text-white bg-white dark:bg-gray-800">
					<Topbar />
				</header>

				<div class="flex-grow">
					<div class="untangled-shell mx-auto flex flex-col gap-4">{props.children}</div>
				</div>
			</div>
		</Show>
	);
};
