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 instanceof TRPCClientError &&
							err.data?.code === "UNAUTHORIZED") ||
						err.data?.code === "FORBIDDEN" ||
						err.data?.code === "CONFLICT" ||
						err.data?.code === "NOT_FOUND"
					) {
						window.location.reload();
					}
					observer.error(err);
				},
				next: observer.next,
			});
			return unsubscribe;
		});
	};
};

export const api = createTRPCNext<AppRouter>({
	config() {
		return {
			links: [
				errorLink,
				loggerLink({
					enabled: (opts) =>
						(process.env.NODE_ENV === "development" &&
							typeof window !== "undefined") ||
						(opts.direction === "down" && opts.result instanceof Error),
				}),

				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>;
