import { trpc } from "@/lib/trpc";
import { UNAUTHED_ERR_MSG } from '@shared/const';
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {
  httpBatchLink,
  httpLink,
  splitLink,
  TRPCClientError,
} from "@trpc/client";
import { createRoot } from "react-dom/client";
import superjson from "superjson";
import App from "./App";
import { getLoginUrl } from "./const";
import { DesignProvider } from "./contexts/DesignContext";
import { captureServerNewsroomFallback } from "./lib/server-newsroom-fallback";
import { installServerPaintOverlay } from "./lib/first-paint-handoff";
import { FirstPaintHandoff } from "./components/FirstPaintHandoff";
import { languageFromPath } from "./i18n";
import "./index.css";

{
  const routeLang = languageFromPath(window.location.pathname);
  document.documentElement.dir = routeLang === "ar" ? "rtl" : "ltr";
  document.documentElement.lang = routeLang;
  localStorage.setItem("wae-lang", routeLang);
}

const NON_RETRYABLE_HTTP = new Set([400, 401, 403, 404, 422]);

function isTransientError(error: unknown): boolean {
  if (error instanceof TRPCClientError) {
    if (error.message === UNAUTHED_ERR_MSG) return false;

    const httpStatus = (error.data as { httpStatus?: number } | undefined)
      ?.httpStatus;
    if (typeof httpStatus === "number" && NON_RETRYABLE_HTTP.has(httpStatus)) {
      return false;
    }

    if (
      typeof error.message === "string" &&
      error.message.includes("is not valid JSON")
    ) {
      return true;
    }

    if (
      typeof error.message === "string" &&
      /timeout|aborted|network|failed to fetch|gateway/i.test(error.message)
    ) {
      return true;
    }
  }

  if (error instanceof DOMException && error.name === "AbortError") return true;
  if (error instanceof TypeError) return true;

  return false;
}

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 5 * 60 * 1000,
      gcTime: 10 * 60 * 1000,
      refetchOnWindowFocus: false,
      refetchOnMount: false,
      retry: (failureCount, error) => {
        if (failureCount >= 4) return false;
        return isTransientError(error);
      },
      retryDelay: attempt => Math.min(1000 * 2 ** attempt, 4000),
    },
    mutations: {
      retry: (failureCount, error) => {
        if (failureCount >= 2) return false;
        return isTransientError(error);
      },
      retryDelay: attempt => Math.min(1000 * 2 ** attempt, 4000),
    },
  },
});

const redirectToLoginIfUnauthorized = (error: unknown) => {
  if (!(error instanceof TRPCClientError)) return;
  if (typeof window === "undefined") return;

  const isUnauthorized = error.message === UNAUTHED_ERR_MSG;
  if (!isUnauthorized) return;
  window.location.href = getLoginUrl();
};

queryClient.getQueryCache().subscribe(event => {
  if (event.type === "updated" && event.action.type === "error") {
    const error = event.query.state.error;
    redirectToLoginIfUnauthorized(error);
    console.error("[API Query Error]", error);
  }
});

queryClient.getMutationCache().subscribe(event => {
  if (event.type === "updated" && event.action.type === "error") {
    const error = event.mutation.state.error;
    redirectToLoginIfUnauthorized(error);
    console.error("[API Mutation Error]", error);
  }
});

const FETCH_TIMEOUT_MS = 25_000;

function fetchWithTimeout(
  input: RequestInfo | URL,
  init?: RequestInit
): Promise<Response> {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);

  const callerSignal = init?.signal;
  if (callerSignal) {
    if (callerSignal.aborted) controller.abort();
    else
      callerSignal.addEventListener("abort", () => controller.abort(), {
        once: true,
      });
  }

  return globalThis
    .fetch(input, {
      ...(init ?? {}),
      credentials: "include",
      signal: controller.signal,
    })
    .finally(() => clearTimeout(timer));
}

export const PUBLIC_TRPC_PROCEDURES = new Set([
  "system.health",
  "assets.videoSignedUrl",
  "brands.bySlug",
  "newsroom.bySlug",
  "inquiries.create",
  "seo.byKey",
  "content.list",
  "newsletter.subscribe",
  "analytics.track",
]);

const analyticsLink = httpLink({
  url: "/api/public/trpc",
  transformer: superjson,
  fetch: fetchWithTimeout,
});

const publicLink = httpBatchLink({
  url: "/api/public/trpc",
  transformer: superjson,
  fetch: fetchWithTimeout,
});

const adminLink = httpBatchLink({
  url: "/api/trpc",
  transformer: superjson,
  fetch: fetchWithTimeout,
});

const trpcClient = trpc.createClient({
  links: [
    splitLink({
      condition(operation) {
        return operation.path === "analytics.track";
      },
      true: analyticsLink,
      false: splitLink({
        condition(operation) {
          return PUBLIC_TRPC_PROCEDURES.has(operation.path);
        },
        true: publicLink,
        false: adminLink,
      }),
    }),
  ],
});

const root = document.getElementById("root")!;
captureServerNewsroomFallback(root, window.location.pathname);
const serverPaintInstalled = installServerPaintOverlay(root);

createRoot(root).render(
  <trpc.Provider client={trpcClient} queryClient={queryClient}>
    <QueryClientProvider client={queryClient}>
      <DesignProvider>
        <App />
        {serverPaintInstalled ? <FirstPaintHandoff /> : null}
      </DesignProvider>
    </QueryClientProvider>
  </trpc.Provider>
);
