"use client";

import * as React from "react";
import { useParams, useRouter } from "next/navigation";
import Link from "next/link";
import { signIn, useSession } from "next-auth/react";
import { CheckCircle2, Loader2, Mail, ShieldAlert } from "lucide-react";

import { Button } from "@/components/ui/button";
import { api } from "@/lib/trpc";

/**
 * Public-by-token invite redeem page. Three phases:
 *
 *   1. Not signed in -> bounce to /sign-in with callbackUrl that brings us
 *      back here. We don't surface the invite contents pre-auth; the only
 *      thing the operator needs to see is "you need to sign in first."
 *
 *   2. Signed in, calling invites.accept(). On success: route to the
 *      workspace landing page.
 *
 *   3. Signed in but identity mismatch (FORBIDDEN with cause.reason ===
 *      "email_not_owned"). Render the explainer with a deep link to the
 *      profile's Linked Emails section. The user can either:
 *        - Sign out and sign in via the matching email's provider.
 *        - Add the missing email to their profile (manual verification
 *          is a follow-up task; in v1 the link points at the read-only
 *          Linked Emails page).
 */
export default function InviteAcceptPage() {
  const params = useParams();
  const router = useRouter();
  const { data: session, status } = useSession();
  const rawToken = params?.token;
  const token = typeof rawToken === "string" ? rawToken : undefined;

  const acceptMut = api.invites.accept.useMutation({
    onSuccess: (result) => {
      router.replace(`/${result.workspace.slug}`);
    },
  });

  const triedRef = React.useRef(false);
  React.useEffect(() => {
    if (!token) return;
    if (status !== "authenticated") return;
    if (triedRef.current) return;
    triedRef.current = true;
    acceptMut.mutate({ token });
  }, [token, status]); // eslint-disable-line react-hooks/exhaustive-deps

  if (!token) {
    return <InviteShell title="Invalid invite link" tone="error" />;
  }

  if (status === "loading") {
    return <InviteShell title="Checking your session…" tone="loading" />;
  }

  if (status === "unauthenticated") {
    return (
      <InviteShell title="Sign in to accept this invite" tone="loading">
        <p className="text-sm text-muted-foreground">
          You need to be signed in for us to know which account to add to the
          workspace.
        </p>
        <div className="mt-4 flex justify-center">
          <Button
            type="button"
            onClick={() =>
              signIn(undefined, { callbackUrl: `/invite/${token}` })
            }
          >
            Sign in to continue
          </Button>
        </div>
      </InviteShell>
    );
  }

  if (acceptMut.isPending || (acceptMut.isIdle && status === "authenticated")) {
    return <InviteShell title="Accepting your invite…" tone="loading" />;
  }

  if (acceptMut.isSuccess && acceptMut.data) {
    return (
      <InviteShell title="You're in" tone="success">
        <p className="text-sm text-muted-foreground">
          Joined {acceptMut.data.workspace.name} as{" "}
          <span className="font-medium">{acceptMut.data.role}</span>. Redirecting…
        </p>
      </InviteShell>
    );
  }

  if (acceptMut.error) {
    // Identity mismatch — caller is signed in but doesn't own the invited
    // email. This is the dedicated explainer path, not a generic error.
    const cause = acceptMut.error.shape?.data?.cause as
      | { reason?: string; invitedEmail?: string }
      | undefined;
    if (cause?.reason === "email_not_owned" && cause.invitedEmail) {
      return (
        <InviteShell
          title="This invite was sent to a different email"
          tone="mismatch"
        >
          <p className="text-sm text-muted-foreground">
            The invite was for{" "}
            <span className="font-medium">{cause.invitedEmail}</span>, but
            you&apos;re signed in as{" "}
            <span className="font-medium">{session?.user?.email}</span>.
          </p>
          <p className="mt-2 text-sm text-muted-foreground">
            To accept, link the invited email to your account from your
            profile (e.g. sign in via that email&apos;s OAuth provider), then
            return to this page. Or sign out and sign back in with the
            invited email directly.
          </p>
          <div className="mt-5 flex flex-wrap justify-center gap-2">
            <Button asChild type="button" variant="outline">
              <Link href="/">Go to my workspaces</Link>
            </Button>
            <Button asChild type="button">
              {/* No global profile route; use the first workspace's profile
                  settings. The user will see Linked Emails there. */}
              <Link href="/">View linked emails</Link>
            </Button>
          </div>
        </InviteShell>
      );
    }

    return (
      <InviteShell title="We couldn't accept this invite" tone="error">
        <p className="text-sm text-muted-foreground">{acceptMut.error.message}</p>
        <div className="mt-4 flex justify-center">
          <Button asChild type="button" variant="outline">
            <Link href="/">Go home</Link>
          </Button>
        </div>
      </InviteShell>
    );
  }

  return <InviteShell title="Loading invite…" tone="loading" />;
}

type Tone = "loading" | "success" | "error" | "mismatch";

function InviteShell({
  title,
  tone,
  children,
}: {
  title: string;
  tone: Tone;
  children?: React.ReactNode;
}) {
  const Icon =
    tone === "success"
      ? CheckCircle2
      : tone === "mismatch"
        ? ShieldAlert
        : tone === "error"
          ? ShieldAlert
          : tone === "loading"
            ? Loader2
            : Mail;
  const iconClass =
    tone === "success"
      ? "text-emerald-600"
      : tone === "error" || tone === "mismatch"
        ? "text-amber-600"
        : "text-muted-foreground";

  return (
    <div className="flex min-h-screen items-center justify-center bg-background px-6">
      <div className="w-full max-w-md rounded-xl border border-border bg-card p-8 text-center shadow-sm">
        <div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted">
          <Icon
            className={`size-6 ${iconClass} ${tone === "loading" ? "animate-spin" : ""}`}
            aria-hidden
          />
        </div>
        <h1 className="mb-2 text-lg font-semibold">{title}</h1>
        {children}
      </div>
    </div>
  );
}
