/**
 * Activity in history mode: the persisted queries the URL's filters select,
 * paged by keyset cursor.
 *
 * The filters arrive already applied — the URL is the applied state — so this
 * only reads them. Everything about how the reader got here lives one level up.
 */

import { useInfiniteQuery } from "@tanstack/react-query";
import { Link } from "@tanstack/react-router";
import * as stylex from "@stylexjs/stylex";
import * as api from "@/lib/api";
import CoverageNotice from "@/lib/CoverageNotice";
import InlineError from "@/lib/InlineError";
import { queriesInfiniteQuery } from "@/lib/queries";
import type { QueryRow } from "@/lib/types";
import { useClientNames } from "@/features/clients/clientNames";
import { summarizeRow } from "@/features/provenance/querySummary";
import { styles as shared } from "@/ui/styles";
import { colors } from "@/ui/tokens.stylex";
import { ActivityCells, ActivityTableHead, activityDomainLink } from "./cells";
import { queriesFilterOf, type ActivitySearch } from "./search";

const styles = stylex.create({
	empty: {
		marginTop: "1.5rem",
		color: colors.textMuted,
	},
	tableWrap: {
		marginTop: "1rem",
		overflowX: "auto",
		borderRadius: "0.25rem",
		borderWidth: 1,
		borderStyle: "solid",
		borderColor: colors.border,
	},
	table: {
		width: "100%",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
	},
	/** `divide-y`: a hairline between rows, so the first row carries none. */
	row: {
		borderTopWidth: { default: 1, ":first-child": 0 },
		borderTopStyle: "solid",
		borderTopColor: colors.border,
	},
	footer: {
		marginTop: "0.75rem",
		display: "flex",
		alignItems: "center",
		gap: "0.75rem",
	},
	note: {
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.textMuted,
	},
	/** The gap a line standing on its own needs from the block above it. */
	spacedTop: {
		marginTop: "0.75rem",
	},
	moreButton: {
		fontWeight: 500,
	},
	moreError: {
		marginTop: "0.5rem",
		fontSize: "0.875rem",
		lineHeight: "1.25rem",
		color: colors.dangerText,
	},
});

function errorMessage(error: unknown): string {
	return error instanceof Error ? error.message : String(error);
}

export default function HistoryActivity({ search }: { search: ActivitySearch }) {
	const filter = queriesFilterOf(search);
	const base = useInfiniteQuery(queriesInfiniteQuery(filter));
	const clientNames = useClientNames();

	const pages = base.data?.pages ?? [];
	const rows: QueryRow[] = pages.flatMap((page) => page.queries);
	// Only from the settled response. Placeholder pages belong to the previous
	// filter, and a watermark is a claim about the window being displayed.
	const coverage = base.isPlaceholderData ? undefined : pages[0]?.coverage;
	const filterActive = Object.keys(filter).length > 0;
	// `base.hasNextPage` reads the query state, which is empty while placeholder
	// data stands in for a filter change; derive the cursor from what is on
	// screen so the button keeps its place instead of flashing "end of log".
	const lastPage = pages[pages.length - 1];
	const hasMore = lastPage !== undefined && lastPage.next_before !== null;
	// A 401 is already redirecting via the cache-level handleUnauthorized.
	const isUnauthorized = base.error instanceof api.ApiError && base.error.status === 401;
	const moreError = base.isFetchNextPageError && !isUnauthorized ? errorMessage(base.error) : null;

	function loadMore() {
		if (!hasMore || base.isFetchingNextPage || base.isPlaceholderData) return;
		void base.fetchNextPage();
	}

	// The loader starts this fetch but does not wait for it, so both the first
	// paint and a failed first page are this component's to render.
	if (base.status === "error" && base.data === undefined) {
		return <InlineError error={base.error} onRetry={() => void base.refetch()} />;
	}
	if (base.data === undefined) {
		return (
			<p {...stylex.props(styles.empty, shared.pulse)} role="status">
				Loading activity…
			</p>
		);
	}

	return (
		<>
			{/*
			 * A quiet line, never a skeleton: the rows on screen stay put while a
			 * filter change is in flight, so a keystroke must not blank the table
			 * it is narrowing.
			 */}
			{base.isFetching && (
				<p {...stylex.props(styles.note, styles.spacedTop)} role="status">
					Updating…
				</p>
			)}
			{rows.length === 0 ? (
				<>
					<p {...stylex.props(styles.empty)}>
						{filterActive ? "No queries match the current filters." : "No queries logged yet."}
					</p>
					{coverage !== undefined && (
						<div {...stylex.props(styles.spacedTop)}>
							<CoverageNotice coverage={coverage} variant="note" />
						</div>
					)}
				</>
			) : (
				<>
					<div {...stylex.props(styles.tableWrap)}>
						<table {...stylex.props(styles.table)}>
							<ActivityTableHead />
							<tbody>
								{rows.map((row) => (
									<tr key={row.id} {...stylex.props(styles.row)}>
										<ActivityCells
											row={summarizeRow(row)}
											clientNames={clientNames}
											renderDomain={(id, children) =>
												id === null ? (
													children
												) : (
													<Link
														to="/activity/queries/$id"
														params={{ id: String(id) }}
														search={search}
														{...stylex.props(activityDomainLink, shared.focusRing)}
													>
														{children}
													</Link>
												)
											}
										/>
									</tr>
								))}
							</tbody>
						</table>
					</div>
					<div {...stylex.props(styles.footer)}>
						<p {...stylex.props(styles.note)}>
							Showing {rows.length} {rows.length === 1 ? "query" : "queries"}
							{hasMore ? "" : " — end of log"}
						</p>
						{coverage !== undefined && <CoverageNotice coverage={coverage} variant="note" />}
						{hasMore && (
							<button
								type="button"
								onClick={loadMore}
								disabled={base.isFetchingNextPage || base.isPlaceholderData}
								{...stylex.props(shared.button, styles.moreButton, shared.focusRing)}
							>
								{base.isFetchingNextPage ? "Loading…" : "Load more"}
							</button>
						)}
					</div>
					{moreError !== null && (
						<p role="alert" {...stylex.props(styles.moreError)}>
							Failed to load more: {moreError}
						</p>
					)}
				</>
			)}
		</>
	);
}
