/**
 * DiscoverPage — yazi depth-stack view of discoverable podcasts.
 *
 *   depth 0 (current) — category list. Parent pane shows the muted
 *                       placeholder (1/5 slot kept).
 *   depth 1 (current) — podcast results for the drilled category. Parent
 *                       pane = the categories list.
 *   preview            — detail of the hovered item (category summary, or
 *                       podcast detail + subscribe action).
 *
 * Renders entirely through `<PaneRow>`; no bespoke 3-column flexbox JSX
 * remains. `l`/Enter drills in (category → results) or subscribes (on a
 * podcast); `h` pops a depth (noop at 0). j/k move only within the current
 * column. Moving through categories at depth 0 updates the store's selected
 * category so the preview follows.
 */

import { createMemo, For, Show, onMount, onCleanup } from "solid-js";
import { useDiscoverStore, DISCOVER_CATEGORIES } from "@/stores/discover";
import { format } from "date-fns";
import { useTheme } from "@/context/ThemeContext";
import {
	useNavigation,
	NavMode,
	DEPTH_CENTER_PANE,
	type PaneId,
	type DepthFrame,
} from "@/context/NavigationContext";
import { on, off } from "@/utils/event-bus";
import { supportsNerdFonts } from "@/utils/nerd-fonts";
import type { KeybindActionName } from "@/context/KeybindContext";
import { PaneRow } from "@/components/PaneRow";
import { TabListPane } from "@/components/TabPanel";
import { LoadingIndicator } from "@/components/LoadingIndicator";
import { useScrollIntoView } from "@/hooks/useScrollIntoView";
import { useSelectionMarker } from "@/hooks/useSelectionMarker";

export const DiscoverPaneCount = 1;

function DiscoverPage() {
	// Static: detection never changes mid-session.
	const nerd = supportsNerdFonts();
	const discoverStore = useDiscoverStore();
	const { theme } = useTheme();
	const muted = () => theme.muted || theme.text;
	const nav = useNavigation();
	const marker = useSelectionMarker();

	const depth = nav.currentDepth;
	const focus = (d: number = depth()) => nav.depthFocus(d);

	const categories = () => DISCOVER_CATEGORIES;
	const podcasts = () => discoverStore.filteredPodcasts();

	const focusedCatIdx = () =>
		categories().length === 0 ? 0 : Math.min(focus(0), categories().length - 1);
	const focusedCategory = createMemo(() => categories()[focusedCatIdx()]);

	const focusedPodIdx = () =>
		podcasts().length === 0 ? 0 : Math.min(focus(1), podcasts().length - 1);
	const focusedPodcast = createMemo(() => podcasts()[focusedPodIdx()]);

	const curLen = () =>
		depth() === 0 ? categories().length : podcasts().length;

	const ensureFocus = () => {
		if (categories().length > 0 && focus(0) >= categories().length)
			nav.setDepthFocus(categories().length - 1, 0);
		if (podcasts().length > 0 && focus(1) >= podcasts().length)
			nav.setDepthFocus(podcasts().length - 1, 1);
	};
	onMount(ensureFocus);

	// Auto-fetch the featured-shows manifest on first mount (network failure is
	// non-fatal — the list stays empty until the user hits refresh).
	onMount(() => {
		discoverStore.refresh().catch(() => {});
	});

	onMount(() => {
		nav.registerResolver(`${nav.activeTab()}:${DEPTH_CENTER_PANE}`, (i) => {
			if (depth() === 0) return categories()[i]?.id;
			return podcasts()[i]?.id;
		});
	});

	// ── helpers ────────────────────────────────────────────────────────────────
	const formatDate = (d: Date) => format(d, "MMM d, yyyy");

	// ── drill / open ───────────────────────────────────────────────────────────
	function open() {
		if (depth() === 0) {
			const c = focusedCategory();
			if (!c) return;
			discoverStore.setSelectedCategory(c.id);
			nav.pushDepth({ kind: "results", ctx: c.id, focus: 0 } as DepthFrame);
			nav.setActivePane(DEPTH_CENTER_PANE);
			return;
		}
		if (depth() >= 1) {
			const pod = focusedPodcast();
			if (pod) discoverStore.toggleSubscription(pod.id);
		}
	}

	// ── nav.action handler ────────────────────────────────────────────────────
	const PAGE_ACTIONS: Partial<Record<KeybindActionName, () => void>> = {
		"move-down": () => step(1),
		"move-up": () => step(-1),
		"jump-down": () => step(5),
		"jump-up": () => step(-5),
		"page-down": () => step(10),
		"page-up": () => step(-10),
		"goto-top": () => nav.gotoIndex(0, curLen()),
		"goto-bottom": () => nav.gotoIndex(curLen() - 1, curLen()),
		open: () => open(),
		"toggle-select": () => {
			if (depth() >= 1) {
				const pod = focusedPodcast();
				if (pod) nav.toggleSelected(pod.id);
			}
		},
		refresh: () => {
			discoverStore.refresh().catch(() => {});
		},
	};
	function step(delta: number) {
		nav.move(delta, curLen());
		// keep the store's selected category synced with the focused row at depth 0
		if (depth() === 0) {
			const c = focusedCategory();
			if (c) discoverStore.setSelectedCategory(c.id);
		}
	}
	const onAction = (data: {
		action: KeybindActionName;
		pane: PaneId;
		mode: NavMode;
	}) => {
		if (data.pane !== DEPTH_CENTER_PANE) return;
		if (nav.activePane() !== DEPTH_CENTER_PANE) return;
		ensureFocus();
		PAGE_ACTIONS[data.action]?.();
	};
	onMount(() => {
		on("nav.action", onAction);
		onCleanup(() => off("nav.action", onAction));
	});

	// ── render ──────────────────────────────────────────────────────────────────
	const isActive = () => nav.activePane() === DEPTH_CENTER_PANE;
	const focusBg = (i: number, lf: number, active: boolean) =>
		i === lf && active ? theme.primary : i === lf ? theme.border : undefined;
	const focusFg = (i: number, lf: number, active: boolean) =>
		i === lf && active
			? theme.surface
			: i === lf
				? theme.selectedListItemText ?? theme.text
				: theme.text;

	const currentLabel = () =>
		depth() === 0
			? "Categories"
			: `${focusedCategory()?.name ?? "Discover"} · ${podcasts().length}`;

	// ── parent pane: previous-depth list (muted/blank at depth 0) ─────────────
	// Sibling <Show> blocks per depth (the known-good opentui disposal
	// pattern, mirrors Settings): a STABLE fragment root whose inner <Show>
	// children toggle on depth change, so the old subtree is disposed instead
	// of left orphaned next to the new one (single <Show with fallback> and
	// ternary root swaps both leak the previous root).
	const parentContent = () => (
		<>
			<Show when={depth() === 0}>
				<TabListPane muted />
			</Show>
			<Show when={depth() >= 1}>
				<For each={categories()}>
					{(cat, index) => {
						const lf = () => nav.depthFocus(0);
						const ref = useScrollIntoView(() => index() === lf());
						return (
							<box
								ref={ref}
								flexDirection="row"
								gap={1}
								paddingRight={1}
								backgroundColor={focusBg(index(), lf(), false)}
							>
								<text fg={focusFg(index(), nav.depthFocus(0), false)}>
									{index() === nav.depthFocus(0) ? marker() : " "}
								</text>
								{nerd && (
									<text fg={focusFg(index(), nav.depthFocus(0), false)}>
										{cat.icon}
									</text>
								)}
								<text fg={focusFg(index(), nav.depthFocus(0), false)}>
									{cat.name}
								</text>
							</box>
						);
					}}
				</For>
			</Show>
		</>
	);

	// ── current pane ───────────────────────────────────────────────────────────
	const currentContent = () => (
		<>
			{/* depth 0: categories */}
			<Show when={depth() === 0}>
				<For each={categories()}>
					{(cat, index) => {
						const lf = () => focusedCatIdx();
						const ref = useScrollIntoView(() => index() === lf());
						return (
							<box
								ref={ref}
								flexDirection="row"
								gap={1}
								paddingRight={1}
								backgroundColor={focusBg(index(), lf(), isActive())}
								onMouseDown={() => {
									nav.setActivePane(DEPTH_CENTER_PANE);
									nav.setDepthFocus(index(), 0);
									discoverStore.setSelectedCategory(cat.id);
								}}
							>
								<text fg={focusFg(index(), lf(), isActive())}>
									{index() === lf() ? marker() : " "}
								</text>
								{nerd && (
									<text fg={focusFg(index(), lf(), isActive())}>
										{cat.icon}
									</text>
								)}
								<text fg={focusFg(index(), lf(), isActive())}>{cat.name}</text>
							</box>
						);
					}}
				</For>
			</Show>
			{/* depth ≥1: results */}
			<Show when={depth() >= 1}>
				<Show
					when={podcasts().length > 0}
					fallback={
						<box padding={1}>
							<Show
								when={discoverStore.isLoading()}
								fallback={
									<text fg={muted()}>No podcasts found. :refresh</text>
								}
							>
								<LoadingIndicator label="Discovering…" />
							</Show>
						</box>
					}
				>
					<For each={podcasts()}>
						{(podcast, index) => {
							const lf = () => focusedPodIdx();
							const ref = useScrollIntoView(() => index() === lf());
							return (
								<box
									ref={ref}
									flexDirection="column"
									gap={0}
									paddingRight={1}
									backgroundColor={focusBg(index(), lf(), isActive())}
									onMouseDown={() => {
										nav.setActivePane(DEPTH_CENTER_PANE);
										nav.setDepthFocus(index(), 1);
									}}
								>
									<box flexDirection="row" gap={1}>
										<text fg={focusFg(index(), lf(), isActive())}>
											{index() === lf() ? marker() : " "}
										</text>
										<text fg={focusFg(index(), lf(), isActive())}>
											{podcast.title}
										</text>
										<Show when={podcast.isSubscribed}>
											<text
												fg={index() === lf() ? theme.surface : theme.success}
											>
												[+]
											</text>
										</Show>
									</box>
									<Show when={podcast.author}>
										<text
											fg={index() === lf() ? theme.surface : muted()}
											paddingLeft={2}
										>
											by {podcast.author}
										</text>
									</Show>
								</box>
							);
						}}
					</For>
					<Show when={discoverStore.isLoading()}>
						<box paddingLeft={2} paddingTop={1}>
							<LoadingIndicator label="Refreshing…" />
						</box>
					</Show>
				</Show>
			</Show>
		</>
	);

	// ── preview pane ───────────────────────────────────────────────────────────
	const previewContent = () =>
		depth() === 0 ? (
			// depth 0 preview: shows for the hovered category
			<Show
				when={focusedCategory()}
				fallback={
					<box padding={1}>
						<text fg={muted()}>No category focused</text>
					</box>
				}
			>
				{(cat) => (
					<box flexDirection="column" gap={0} padding={1}>
						<text fg={theme.textPrimary ?? theme.text}>
							<strong>{cat().name}</strong>
						</text>
						<Show when={(cat() as any).description}>
							<text fg={theme.textSecondary}>{(cat() as any).description}</text>
						</Show>
						<box height={1} />
						<Show
							when={podcasts().length > 0}
							fallback={
								<text fg={muted()}>
									No shows in this category yet. :refresh
								</text>
							}
						>
							<For each={podcasts()}>
								{(pod) => (
									<box flexDirection="column" gap={0}>
										<text fg={theme.text}>{pod.title}</text>
										<Show when={pod.author}>
											<text fg={muted()} paddingLeft={2}>
												by {pod.author}
											</text>
										</Show>
									</box>
								)}
							</For>
						</Show>
					</box>
				)}
			</Show>
		) : (
			// depth ≥1 preview: hovered podcast + subscribe
			<Show
				when={focusedPodcast()}
				fallback={
					<box padding={1}>
						<text fg={muted()}>No podcast focused</text>
					</box>
				}
			>
				{(pod) => (
					<box flexDirection="column" gap={1} padding={1}>
						<text fg={theme.textPrimary ?? theme.text}>
							<strong>{pod().title}</strong>
						</text>
						<Show when={pod().author}>
							<text fg={muted()}>by {pod().author}</text>
						</Show>
						<Show when={pod().isSubscribed}>
							<text fg={theme.success}>✓ Subscribed</text>
						</Show>
						<Show when={!pod().isSubscribed}>
							<text fg={theme.primary}>[+] Subscribe (enter)</text>
						</Show>
						<box height={1} />
						<text fg={theme.textSecondary}>
							{pod().description?.slice(0, 400) ?? "No description available."}
							{(pod().description?.length ?? 0) > 400 ? "…" : ""}
						</text>
						<Show when={(pod().categories ?? []).length > 0}>
							<box flexDirection="row" gap={1}>
								<For each={(pod().categories ?? []).slice(0, 4)}>
									{(cat) => <text fg={theme.warning}>[{cat}]</text>}
								</For>
							</box>
						</Show>
						<Show when={pod().feedUrl}>
							<text fg={muted()}>Feed: {pod().feedUrl}</text>
						</Show>
						<text fg={muted()}>Updated: {formatDate(pod().lastUpdated)}</text>
						<box height={1} />
						<text fg={muted()}>enter: subscribe · h: back · r: refresh</text>
					</box>
				)}
			</Show>
		);

	return (
		<PaneRow
			parent={parentContent}
			current={currentContent}
			preview={previewContent}
			currentLabel={currentLabel}
			focused={isActive}
		/>
	);
}

export { DiscoverPage };
