"use client";

import { useEffect, useState, type ReactNode } from "react";
import { useQueryClient } from "@tanstack/react-query";

import Box from "@mui/material/Box";
import Dialog from "@mui/material/Dialog";
import Typography from "@mui/material/Typography";

import { useAuthText } from "@/features/auth/auth-i18n";
import { AuthPrimaryButton } from "@/features/auth/components/auth-primary-button";
import {
  SESSION_EXPIRED_EVENT,
  goToLoginAfterSessionExpired,
  isSessionExpired,
} from "@/features/auth/service/auth-service";

// Once the session has ended there is nothing useful left to ask the API: every
// call would come back 401 and screens would swap their content for empty
// states and error messages behind the dialog. So we hold those requests — they
// never settle, which leaves each screen showing the data it already had until
// the user presses "Sign in again" and the page is replaced anyway.
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL ?? "";

let fetchAlreadyPatched = false;

function requestUrl(input: RequestInfo | URL) {
  if (typeof input === "string") return input;
  if (input instanceof URL) return input.toString();

  return input.url;
}

function installApiRequestFreeze() {
  if (fetchAlreadyPatched || typeof window === "undefined" || !API_BASE_URL) {
    return;
  }

  fetchAlreadyPatched = true;

  const originalFetch = window.fetch.bind(window);

  window.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
    if (isSessionExpired() && requestUrl(input).startsWith(API_BASE_URL)) {
      // Deliberately never resolves; the page is on its way out.
      return new Promise<Response>(() => {});
    }

    return originalFetch(input, init);
  };
}

// Shows one dialog, in the middle of whatever page the user is on, when the
// API reports their session has ended. Nothing navigates until they press the
// button, so they are not thrown off the page they were reading.
export function SessionExpiredProvider({
  children,
}: {
  children: ReactNode;
}) {
  const text = useAuthText();
  const queryClient = useQueryClient();
  const [open, setOpen] = useState(false);

  // Installed up front so it is ready the moment the session ends.
  useEffect(() => {
    installApiRequestFreeze();
  }, []);

  useEffect(() => {
    function handleSessionExpired() {
      setOpen(true);

      // Stop anything already in flight, so a half-finished screen does not
      // flip to an error state under the dialog.
      void queryClient.cancelQueries();
    }

    window.addEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);

    return () => {
      window.removeEventListener(SESSION_EXPIRED_EVENT, handleSessionExpired);
    };
  }, [queryClient]);

  return (
    <>
      {children}

      <Dialog
        open={open}
        // No onClose: signing in again is the only way out, so the dialog does
        // not close on a stray click while the session is dead.
        maxWidth={false}
        fullWidth
        slotProps={{
          paper: { sx: { maxWidth: 512, borderRadius: "12px", p: 3 } },
        }}
      >
        {/* Heading and the reason, so the user knows nothing was lost. */}
        <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
          <Typography
            component="h2"
            sx={{
              fontSize: 18,
              fontWeight: 600,
              lineHeight: 1.4,
              color: "text.primary",
            }}
          >
            {text.sessionExpiredTitle}
          </Typography>

          <Typography
            sx={{ fontSize: 14, lineHeight: 1.6, color: "text.secondary" }}
          >
            {text.sessionExpiredDescription}
          </Typography>
        </Box>

        {/* One full-width action: the only way out of this dialog. */}
        <Box sx={{ mt: 3 }}>
          <AuthPrimaryButton onClick={goToLoginAfterSessionExpired}>
            {text.signInAgain}
          </AuthPrimaryButton>
        </Box>
      </Dialog>
    </>
  );
}
