"use client";

import {
  ReactNode,
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { Input, Spinner } from "@nextui-org/react";
import { FaEnvelope, FaKey, FaTriangleExclamation } from "react-icons/fa6";

import Button from "@/components/common/button";
import ClientPreviewBanner from "@/components/pages/client/participant-list/common/client-preview-banner";
import { INTERNAL_PAGES } from "@/constants/pages-mapping/internal-pages-mapping";
import {
  ClientPortalAccess,
  PortalAuth,
  bootstrapPortalAccess,
  clearPortalBearerSession,
  clearPreviewPortalSession,
  requestPortalOtp,
  restorePortalBearerSession,
  verifyPortalOtp,
} from "@/services/client/participant-list/portal-actions";

type AuthPhase =
  | "restoring"
  | "email"
  | "otp"
  | "bootstrap-error"
  | "ready"
  | "unavailable";

type ClientPortalAuthContextValue = {
  auth: PortalAuth | null;
  access: ClientPortalAccess | null;
  canEdit: boolean;
  handlePortalStatus: (status: number) => boolean;
  mode: "client" | "internalPreview";
  routes: { home: string; participants: string };
};

const ClientPortalAuthContext =
  createContext<ClientPortalAuthContextValue | null>(null);

type ClientPortalAuthProviderProps = {
  magicLinkId: string | null;
  mode?: "client" | "internalPreview";
  previewExpiresAt?: string | null;
  children: ReactNode;
};

const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

function AuthShell({ children }: { children: ReactNode }) {
  return (
    <div className="flex min-h-screen items-center justify-center bg-others-mistyGray px-4 py-12 font-noto dark:bg-jet">
      <div className="w-full max-w-md rounded-2xl border border-gray-200 bg-white p-6 shadow-sm dark:border-neutral-700 dark:bg-neutral-900 sm:p-8">
        {children}
      </div>
    </div>
  );
}

export function ClientPortalAuthProvider({
  magicLinkId,
  mode = "client",
  previewExpiresAt = null,
  children,
}: ClientPortalAuthProviderProps) {
  const [phase, setPhase] = useState<AuthPhase>("restoring");
  const [auth, setAuth] = useState<PortalAuth | null>(null);
  const [access, setAccess] = useState<ClientPortalAccess | null>(null);
  const [email, setEmail] = useState("");
  const [code, setCode] = useState("");
  const [feedback, setFeedback] = useState<string | null>(null);
  const [permissionFeedback, setPermissionFeedback] = useState<string | null>(
    null,
  );
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [isPreviewRevalidating, setIsPreviewRevalidating] = useState(false);
  const previewRevalidationInFlight = useRef(false);

  const clearStoredSession = useCallback(() => {
    if (!magicLinkId) return;
    try {
      sessionStorage.removeItem(`pfr_client_portal_session_${magicLinkId}`);
    } catch {
      // Storage can be unavailable in hardened/private browser modes.
    }
  }, [magicLinkId]);

  const clearPersistedSession = useCallback(() => {
    clearStoredSession();
    void clearPortalBearerSession(magicLinkId).catch(() => {
      // Clearing persistence is best-effort when the network is unavailable.
    });
  }, [clearStoredSession, magicLinkId]);

  const enterEmailStep = useCallback(
    (message?: string) => {
      // A delayed 401 from an earlier request must not erase a newer OTP
      // session for the same link. Definitive 404/410 branches clear cookies.
      clearStoredSession();
      setAuth(null);
      setAccess(null);
      setCode("");
      setPermissionFeedback(null);
      setFeedback(message ?? null);
      setPhase("email");
    },
    [clearStoredSession],
  );

  const leavePreview = useCallback((target: string) => {
    setAuth(null);
    setAccess(null);
    setPhase("restoring");
    void clearPreviewPortalSession().finally(() => {
      window.location.replace(target);
    });
  }, []);

  const applyBootstrap = useCallback(
    async (
      nextAuth: PortalAuth,
      hasSavedSession: boolean,
    ): Promise<boolean> => {
      setAuth(nextAuth);
      try {
        const result = await bootstrapPortalAccess(nextAuth);
        if (result.ok) {
          setAccess(result.data);
          setPermissionFeedback(null);
          setFeedback(null);
          setPhase("ready");
          return true;
        }
        if (mode === "internalPreview") {
          if (result.status === 401 || result.status === 410) {
            leavePreview(INTERNAL_PAGES.client.participantList.preview.expired);
          } else if (result.status === 403) {
            leavePreview(
              `${INTERNAL_PAGES.client.participantList.preview.unavailable}?reason=access-revoked`,
            );
          } else {
            setAccess(null);
            setFeedback(
              result.status === 429
                ? "Too many preview requests were made. Wait a moment, then try again or return to Hub."
                : "We couldn't open the preview. Your preview session is still available; try again or return to Hub.",
            );
            setPhase("bootstrap-error");
          }
        } else if (result.status === 401) {
          enterEmailStep("Your session ended. Please request a new code.");
        } else if (result.status === 404 || result.status === 410) {
          clearPersistedSession();
          setAuth(null);
          setAccess(null);
          setPhase("unavailable");
        } else {
          setAccess(null);
          setFeedback(
            hasSavedSession
              ? "We couldn't finish opening your portal. Your verified session is still saved."
              : "We couldn't finish opening your portal. Please try again.",
          );
          setPhase("bootstrap-error");
        }
      } catch {
        setAccess(null);
        setFeedback(
          mode === "internalPreview"
            ? "We couldn't open the preview. Your preview session is still available; try again or return to Hub."
            : hasSavedSession
              ? "We couldn't finish opening your portal. Your verified session is still saved."
              : "We couldn't finish opening your portal. Please try again.",
        );
        setPhase("bootstrap-error");
      }
      return false;
    },
    [clearPersistedSession, enterEmailStep, leavePreview, mode],
  );

  useEffect(() => {
    if (mode === "internalPreview") {
      void applyBootstrap({ mode: "internalPreview" }, true);
      return;
    }
    if (!magicLinkId) {
      setPhase("unavailable");
      return;
    }

    let active = true;

    const restore = async () => {
      clearStoredSession();
      try {
        const restored = await restorePortalBearerSession(magicLinkId);
        if (restored) {
          if (active) {
            await applyBootstrap({ mode: "client", magicLinkId }, true);
          }
          return;
        }
      } catch {
        // A restore failure falls back to a fresh OTP request.
      }

      if (!active) return;
      setPhase("email");
    };

    void restore();
    return () => {
      active = false;
    };
  }, [applyBootstrap, clearStoredSession, magicLinkId, mode]);

  const handlePortalStatus = useCallback(
    (status: number): boolean => {
      if (mode === "internalPreview") {
        if (status === 401 || status === 410) {
          leavePreview(INTERNAL_PAGES.client.participantList.preview.expired);
          return true;
        }
        if (status === 403) {
          leavePreview(
            `${INTERNAL_PAGES.client.participantList.preview.unavailable}?reason=access-revoked`,
          );
          return true;
        }
        // A scoped participant/session miss is an in-preview 404 and must not
        // reveal or revoke anything outside the current preview.
        if (status === 404) return false;

        setAccess(null);
        setFeedback(
          status === 429
            ? "Too many preview requests were made. Wait a moment, then try again or return to Hub."
            : "We couldn't load the preview. Your preview session is still available; try again or return to Hub.",
        );
        setPhase("bootstrap-error");
        return true;
      }

      if (status === 401) {
        enterEmailStep("Your session ended. Please request a new code.");
        return true;
      }
      if (status === 403) {
        setAccess((current) =>
          current ? { ...current, canEdit: false } : current,
        );
        setPermissionFeedback(
          "You no longer have permission to make changes. The requested change was not saved.",
        );
        return true;
      }
      if (status === 404 || status === 410) {
        clearPersistedSession();
        setAuth(null);
        setAccess(null);
        setPhase("unavailable");
        return true;
      }
      return false;
    },
    [clearPersistedSession, enterEmailStep, leavePreview, mode],
  );

  useEffect(() => {
    if (
      mode !== "internalPreview" ||
      phase !== "ready" ||
      auth?.mode !== "internalPreview"
    ) {
      return;
    }

    let active = true;
    const revalidate = () => {
      if (previewRevalidationInFlight.current) return;
      previewRevalidationInFlight.current = true;
      setIsPreviewRevalidating(true);
      void applyBootstrap(auth, true).finally(() => {
        previewRevalidationInFlight.current = false;
        if (active) setIsPreviewRevalidating(false);
      });
    };
    const onVisibilityChange = () => {
      if (document.visibilityState === "visible") revalidate();
    };
    const onPageShow = (event: PageTransitionEvent) => {
      if (event.persisted) revalidate();
    };

    window.addEventListener("focus", revalidate);
    window.addEventListener("pageshow", onPageShow);
    document.addEventListener("visibilitychange", onVisibilityChange);
    return () => {
      active = false;
      window.removeEventListener("focus", revalidate);
      window.removeEventListener("pageshow", onPageShow);
      document.removeEventListener("visibilitychange", onVisibilityChange);
    };
  }, [applyBootstrap, auth, mode, phase]);

  const handleRequestCode = async () => {
    if (!magicLinkId || !EMAIL_PATTERN.test(email.trim()) || isSubmitting) {
      setFeedback("Please enter a valid email address.");
      return;
    }
    setIsSubmitting(true);
    setFeedback(null);
    try {
      const result = await requestPortalOtp(magicLinkId, email);
      if (result.ok) {
        setCode("");
        setFeedback(
          "If that email is authorised, a six-digit code has been sent.",
        );
        setPhase("otp");
        return;
      }
      if (result.status === 404 || result.status === 410) {
        clearPersistedSession();
        setAuth(null);
        setAccess(null);
        setPhase("unavailable");
        return;
      }
      setFeedback(
        result.status === 429
          ? "Too many requests. Please wait before trying again."
          : "We couldn't request a code. Please try again.",
      );
    } catch {
      setFeedback("We couldn't request a code. Please try again.");
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleVerifyCode = async () => {
    if (!magicLinkId || code.length !== 6 || isSubmitting) {
      setFeedback("Enter the six-digit code from your email.");
      return;
    }
    setIsSubmitting(true);
    setFeedback(null);
    try {
      const result = await verifyPortalOtp(magicLinkId, email, code);
      if (!result.ok) {
        if (result.status === 401) {
          enterEmailStep("Your session ended. Please request a new code.");
        } else if (result.status === 404 || result.status === 410) {
          clearPersistedSession();
          setAuth(null);
          setAccess(null);
          setPhase("unavailable");
        } else if (result.sessionSaved) {
          setAuth({ mode: "client", magicLinkId });
          setAccess(null);
          setFeedback(
            "We couldn't finish opening your portal. Your verified session is still saved.",
          );
          setPhase("bootstrap-error");
        } else {
          setFeedback(
            "The code couldn't be verified. Check it or request a new code.",
          );
        }
        return;
      }
      setAuth({ mode: "client", magicLinkId });
      setAccess(result.data);
      setPermissionFeedback(null);
      setFeedback(null);
      setPhase("ready");
    } catch {
      if (auth) {
        setFeedback(
          "We couldn't finish opening your portal. Please try again.",
        );
        setPhase("bootstrap-error");
      } else {
        setFeedback(
          "The code couldn't be verified. Check it or request a new code.",
        );
      }
    } finally {
      setIsSubmitting(false);
    }
  };

  const handleRetryBootstrap = async () => {
    if (!auth || isSubmitting) return;
    setIsSubmitting(true);
    setPhase("restoring");
    try {
      await applyBootstrap(auth, true);
    } finally {
      setIsSubmitting(false);
    }
  };

  const handlePreviewExpiry = useCallback(() => {
    leavePreview(INTERNAL_PAGES.client.participantList.preview.expired);
  }, [leavePreview]);

  const effectivePreviewExpiry =
    mode === "internalPreview" && access && "mode" in access
      ? access.expiresAt
      : previewExpiresAt;

  const value = useMemo<ClientPortalAuthContextValue>(
    () => ({
      auth,
      access,
      canEdit: mode === "client" && access?.canEdit === true,
      handlePortalStatus,
      mode,
      routes:
        mode === "internalPreview"
          ? {
              home: INTERNAL_PAGES.client.participantList.preview.home,
              participants:
                INTERNAL_PAGES.client.participantList.preview.participants,
            }
          : {
              home: INTERNAL_PAGES.client.participantList.home,
              participants: INTERNAL_PAGES.client.participantList.participants,
            },
    }),
    [access, auth, handlePortalStatus, mode],
  );

  if (phase === "restoring") {
    return (
      <AuthShell>
        <div className="flex flex-col items-center gap-3 py-8 text-center">
          <Spinner color="primary" />
          <p className="text-sm text-gray-600 dark:text-neutral-300">
            Checking your secure session…
          </p>
        </div>
      </AuthShell>
    );
  }

  if (phase === "unavailable") {
    return (
      <AuthShell>
        <div className="text-center">
          <FaTriangleExclamation className="mx-auto mb-5 size-8 text-red-500" />
          <h1 className="font-inter text-2xl font-black text-gray-900 dark:text-white">
            Access link unavailable
          </h1>
          <p className="mt-3 text-sm text-gray-600 dark:text-neutral-300">
            This link is invalid, expired, or has been revoked. Please contact
            your project manager for a new one.
          </p>
        </div>
      </AuthShell>
    );
  }

  if (phase === "bootstrap-error") {
    return (
      <AuthShell>
        <div className="text-center">
          <FaTriangleExclamation className="mx-auto mb-5 size-8 text-amber-500" />
          <h1 className="font-inter text-2xl font-black text-gray-900 dark:text-white">
            {mode === "internalPreview"
              ? "We couldn't open the preview"
              : "We couldn't open the portal"}
          </h1>
          <p
            className="mt-3 text-sm text-gray-600 dark:text-neutral-300"
            role="status"
          >
            {feedback}
          </p>
          <Button
            btnLabel={
              mode === "internalPreview"
                ? "Try opening the preview again"
                : "Try opening the portal again"
            }
            className="mt-6"
            customVariant="client"
            isDisabled={isSubmitting}
            isLoading={isSubmitting}
            onClick={handleRetryBootstrap}
          />
          {mode === "internalPreview" && (
            <form action="/participant-list/preview/end" method="post">
              <button
                className="mt-4 text-sm font-semibold underline"
                type="submit"
              >
                Return to Hub
              </button>
            </form>
          )}
        </div>
      </AuthShell>
    );
  }

  if (phase !== "ready") {
    const isOtp = phase === "otp";
    return (
      <AuthShell>
        <div className="mb-6 flex size-11 items-center justify-center rounded-full bg-paleBlue/50 text-midnightBlue">
          {isOtp ? <FaKey /> : <FaEnvelope />}
        </div>
        <h1 className="font-inter text-2xl font-black text-gray-900 dark:text-white">
          {isOtp ? "Enter your verification code" : "Verify your email"}
        </h1>
        <p className="mt-2 text-sm text-gray-600 dark:text-neutral-300">
          {isOtp
            ? "Enter the six-digit code sent by email. For your security, never share this code."
            : "Use the email address associated with this research project to request a secure access code."}
        </p>

        <div className="mt-6 space-y-4">
          {isOtp ? (
            <Input
              autoComplete="one-time-code"
              inputMode="numeric"
              label="Six-digit code"
              labelPlacement="outside"
              maxLength={6}
              placeholder="000000"
              value={code}
              variant="bordered"
              onValueChange={(value) =>
                setCode(value.replace(/\D/g, "").slice(0, 6))
              }
            />
          ) : (
            <Input
              autoComplete="email"
              label="Email address"
              labelPlacement="outside"
              placeholder="name@company.com"
              type="email"
              value={email}
              variant="bordered"
              onValueChange={setEmail}
            />
          )}

          {feedback && (
            <p
              className="text-sm text-gray-600 dark:text-neutral-300"
              role="status"
            >
              {feedback}
            </p>
          )}

          <Button
            fullWidth
            btnLabel={isOtp ? "Verify code" : "Send code"}
            customVariant="client"
            isDisabled={
              isSubmitting ||
              (isOtp ? code.length !== 6 : !EMAIL_PATTERN.test(email.trim()))
            }
            isLoading={isSubmitting}
            onClick={isOtp ? handleVerifyCode : handleRequestCode}
          />

          {isOtp && (
            <div className="flex flex-wrap justify-center gap-3">
              <Button
                btnLabel="Request a new code"
                isDisabled={isSubmitting}
                size="sm"
                variant="light"
                onClick={handleRequestCode}
              />
              <Button
                btnLabel="Use a different email"
                isDisabled={isSubmitting}
                size="sm"
                variant="light"
                onClick={() => {
                  setCode("");
                  setFeedback(null);
                  setPhase("email");
                }}
              />
            </div>
          )}
        </div>
      </AuthShell>
    );
  }

  return (
    <ClientPortalAuthContext.Provider value={value}>
      {mode === "internalPreview" && effectivePreviewExpiry && (
        <ClientPreviewBanner
          expiresAt={effectivePreviewExpiry}
          onExpire={handlePreviewExpiry}
        />
      )}
      {permissionFeedback && (
        <div
          className="border-b border-amber-200 bg-amber-50 px-4 py-3 text-center text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-200"
          role="status"
        >
          {permissionFeedback}
        </div>
      )}
      {isPreviewRevalidating ? (
        <div className="flex min-h-[50vh] items-center justify-center">
          <Spinner label="Checking preview access…" />
        </div>
      ) : (
        children
      )}
    </ClientPortalAuthContext.Provider>
  );
}

export function useClientPortalAuth(): ClientPortalAuthContextValue {
  const context = useContext(ClientPortalAuthContext);
  if (!context) {
    throw new Error(
      "useClientPortalAuth must be used within ClientPortalAuthProvider",
    );
  }
  return context;
}
