import {
	httpBatchLink,
	httpSubscriptionLink,
	loggerLink,
	splitLink,
	TRPCClientError,
	type TRPCLink,
} from "@trpc/client";
import { createTRPCNext } from "@trpc/next";
import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import superjson from "superjson";
import type { AppRouter } from "~/server/api/root";

const getUrl = () => {
	const base = (() => {
		if (typeof window !== "undefined") return window.location.origin;
		return `http://localhost:${process.env.PORT ?? 3000}`;
	})();

	return `${base}/api/trpc`;
};

const errorLink: TRPCLink<AppRouter> = () => {
	return ({ next, op }) => {
		return observable((observer) => {
			const unsubscribe = next(op).subscribe({
				complete: observer.complete,
				error(err) {
					if (err.message.startsWith("SESSION_EXPIRED")) {
						// Silently reload — the auth proxy will refresh the token
						window.location.reload();
						return;
					}
					if (
						(err instanceof TRPCClientError &&
							err.data?.code === "UNAUTHORIZED") ||
						err.data?.code === "FORBIDDEN" ||
						err.data?.code === "CONFLICT" ||
						err.data?.code === "NOT_FOUND"
					) {
						console.warn(
							`Received ${err.data?.code} error from tRPC API, reloading the page to trigger auth flow or show error page.`,
							err,
						);
						window.location.reload();
						return;
					}
					observer.error(err);
				},
				next: observer.next,
			});
			return unsubscribe;
		});
	};
};

export const api = createTRPCNext<AppRouter>({
	config() {
		return {
			links: [
				errorLink,
				loggerLink({
					enabled: (opts) => {
						const isDev = process.env.NODE_ENV === "development";

						if (opts.direction === "down" && opts.result instanceof Error) {
							const isIgnoredError =
								opts.result.message.startsWith("SESSION_EXPIRED") ||
								opts.result.data?.code === "UNAUTHORIZED";
							if (!isIgnoredError) {
								return true;
							}
							if (isDev) {
								console.warn("Following error is ignored in production:");
								return true;
							}
							return false;
						}
						if (isDev) {
							return true;
						}
						return false;
					},
				}),

				splitLink({
					// uses the httpSubscriptionLink for subscriptions
					condition: (op) => op.type === "subscription",
					false: httpBatchLink({
						transformer: superjson,
						url: getUrl(),
					}),
					true: httpSubscriptionLink({
						transformer: superjson,
						url: getUrl(),
					}),
				}),
			],
			queryClientConfig: {
				defaultOptions: {
					queries: {
						refetchOnWindowFocus: false,
					},
				},
			},
		};
	},
	/**
	 * Whether tRPC should await queries when server rendering pages.
	 */
	ssr: false,
	transformer: superjson,
});

/**
 * Inference helper for inputs.
 *
 * @example type HelloInput = RouterInputs['example']['hello']
 */
export type RouterInputs = inferRouterInputs<AppRouter>;

/**
 * Inference helper for outputs.
 *
 * @example type HelloOutput = RouterOutputs['example']['hello']
 */
export type RouterOutputs = inferRouterOutputs<AppRouter>;
