import {
	Component,
	type ErrorInfo,
	type ReactNode,
	lazy,
	Suspense,
	useEffect,
	useRef,
} from "react";
import {
	Navigate,
	NavLink,
	Route,
	Routes,
	useLocation,
} from "react-router-dom";

import type { BackupToolClient, Components } from "../api/generated/client";
import type { OperatorPageProps } from "./OperatorPages";

type SessionUser = Components["schemas"]["SessionUser"];

type RouteDefinition = {
	path: string;
	label: string;
	title: string;
	component: React.LazyExoticComponent<React.ComponentType<OperatorPageProps>>;
};

const DashboardPage = lazy(() =>
	import("./OperatorPages").then(({ DashboardPage: Page }) => ({
		default: Page,
	})),
);
const SourcesPage = lazy(() =>
	import("./OperatorPages").then(({ SourcesPage: Page }) => ({
		default: Page,
	})),
);
const RepositoriesPage = lazy(() =>
	import("./OperatorPages").then(({ RepositoriesPage: Page }) => ({
		default: Page,
	})),
);
const JobsPage = lazy(() =>
	import("./OperatorPages").then(({ JobsPage: Page }) => ({ default: Page })),
);
const ExecutionsPage = lazy(() =>
	import("./OperatorPages").then(({ ExecutionsPage: Page }) => ({
		default: Page,
	})),
);
const BackupsPage = lazy(() =>
	import("./M13Workflows").then(({ BackupsPage: Page }) => ({ default: Page })),
);
const SecurityPage = lazy(() =>
	import("./M13Workflows").then(({ SecurityPage: Page }) => ({
		default: Page,
	})),
);
const NotificationsPage = lazy(() =>
	import("./M13Workflows").then(({ NotificationsPage: Page }) => ({
		default: Page,
	})),
);
const AuditPage = lazy(() =>
	import("./M13Workflows").then(({ AuditPage: Page }) => ({ default: Page })),
);
const AdministrationPage = lazy(() =>
	import("./AdministrationPage").then(({ AdministrationPage: Page }) => ({
		default: Page,
	})),
);

const routes: RouteDefinition[] = [
	{
		path: "/dashboard",
		label: "Dashboard",
		title: "Dashboard",
		component: DashboardPage,
	},
	{
		path: "/sources",
		label: "Sources",
		title: "Sources",
		component: SourcesPage,
	},
	{
		path: "/repositories",
		label: "Repositories",
		title: "Repositories",
		component: RepositoriesPage,
	},
	{
		path: "/jobs",
		label: "Jobs & schedules",
		title: "Jobs & schedules",
		component: JobsPage,
	},
	{
		path: "/executions",
		label: "Executions",
		title: "Executions",
		component: ExecutionsPage,
	},
	{
		path: "/backups",
		label: "Backups",
		title: "Backups",
		component: BackupsPage,
	},
	{
		path: "/security",
		label: "Security & recovery",
		title: "Security & recovery",
		component: SecurityPage,
	},
	{
		path: "/notifications",
		label: "Notifications",
		title: "Notifications",
		component: NotificationsPage,
	},
	{ path: "/audit", label: "Audit", title: "Audit", component: AuditPage },
	{
		path: "/administration",
		label: "Administration",
		title: "Administration",
		component: AdministrationPage,
	},
];

class RouteErrorBoundary extends Component<
	{ children: ReactNode },
	{ error?: Error }
> {
	state: { error?: Error } = {};

	static getDerivedStateFromError(error: Error) {
		return { error };
	}

	componentDidCatch(_error: Error, _info: ErrorInfo) {}

	componentDidUpdate(previousProps: Readonly<{ children: ReactNode }>) {
		if (this.state.error && previousProps.children !== this.props.children)
			this.setState({ error: undefined });
	}

	render() {
		if (this.state.error)
			return (
				<section className="mx-auto max-w-6xl p-4 sm:p-6">
					<h2 className="text-xl font-semibold">Page unavailable</h2>
					<p className="mt-3" role="alert">
						This page could not be loaded. Try another page or refresh your
						browser.
					</p>
				</section>
			);
		return this.props.children;
	}
}

function RouteLoading() {
	return (
		<section className="mx-auto max-w-6xl p-4 sm:p-6">
			<p aria-live="polite" role="status">
				Loading page…
			</p>
		</section>
	);
}

function SkipLink() {
	return (
		<a
			className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-10 focus:rounded focus:bg-slate-100 focus:px-4 focus:py-2 focus:text-slate-950"
			href="#main-content"
			onClick={(event) => {
				event.preventDefault();
				document.getElementById("main-content")?.focus();
			}}
		>
			Skip to main content
		</a>
	);
}

function PageRoute({
	component: Page,
	...props
}: RouteDefinition & OperatorPageProps) {
	return (
		<RouteErrorBoundary>
			<Suspense fallback={<RouteLoading />}>
				<Page {...props} />
			</Suspense>
		</RouteErrorBoundary>
	);
}

function RouteEffects() {
	const { pathname } = useLocation();
	const hasMounted = useRef(false);

	useEffect(() => {
		const route = routes.find((item) => item.path === pathname);
		document.title = `${route?.title ?? "Dashboard"} | Backup Tool`;
		if (hasMounted.current) document.getElementById("main-content")?.focus();
		hasMounted.current = true;
	}, [pathname]);

	return null;
}

export function OperatorViews({
	client,
	onSessionExpired,
	user,
}: {
	client: BackupToolClient;
	onSessionExpired: () => void;
	user: SessionUser;
}) {
	const pageProps = { client, onSessionExpired };
	return (
		<div className="min-h-screen bg-slate-950 text-slate-100">
			<SkipLink />
			<header className="border-b border-slate-800 bg-slate-900">
				<div className="mx-auto flex max-w-6xl flex-wrap items-center justify-between gap-4 px-4 py-5 sm:px-6">
					<div>
						<p className="text-sm font-semibold uppercase tracking-widest text-emerald-400">
							Backup Tool
						</p>
						<h1 className="mt-1 text-2xl font-bold">Operator console</h1>
					</div>
					<p className="text-sm text-slate-300">
						<span className="sr-only">Signed in as </span>
						{user.username}
					</p>
				</div>
				<nav
					aria-label="Primary"
					className="mx-auto flex max-w-6xl gap-1 overflow-x-auto px-4 pb-3 sm:px-6"
				>
					{routes.map((item) => (
						<NavLink
							className={({ isActive }) =>
								`whitespace-nowrap rounded-md px-3 py-2 text-sm font-semibold hover:bg-slate-800 focus-visible:bg-slate-800 ${isActive ? "bg-slate-800 text-emerald-300" : ""}`
							}
							key={item.path}
							to={item.path}
						>
							{item.label}
						</NavLink>
					))}
				</nav>
			</header>
			<main id="main-content" tabIndex={-1}>
				<RouteEffects />
				<Routes>
					<Route element={<Navigate replace to="/dashboard" />} path="/" />
					{routes.map((route) => (
						<Route
							element={<PageRoute {...route} {...pageProps} />}
							key={route.path}
							path={route.path}
						/>
					))}
					<Route element={<Navigate replace to="/dashboard" />} path="*" />
				</Routes>
			</main>
		</div>
	);
}
