/**
 * GET /api/integrations/github/callback?code=…&state=…
 *
 * Final step of the OAuth dance:
 *   1. Validate the `state` matches what we stored in the cookie.
 *   2. Exchange `code` for an access token.
 *   3. Read the GitHub /user endpoint to confirm + capture login.
 *   4. Encrypt the token and persist on fs_users.data.integrations.github.
 *   5. Redirect back to the `returnTo` URL we stashed in the state cookie.
 *
 * On any failure we redirect back to /projects with ?gh_error=… so the
 * UI can surface the message in a toast.
 */

import { NextResponse } from "next/server";
import { authSession } from "@/lib/auth/session-server";
import {
  exchangeCodeForToken,
  getAuthenticatedUser,
  persistGithubIntegration,
  isGithubOauthConfigured,
} from "@/lib/integrations/github";

const STATE_COOKIE = "gh_oauth_state";

function bounce(
  origin: string,
  returnTo: string,
  params: Record<string, string>,
): NextResponse {
  const dest = new URL(returnTo.startsWith("/") ? returnTo : "/", origin);
  for (const [k, v] of Object.entries(params)) dest.searchParams.set(k, v);
  const res = NextResponse.redirect(dest);
  // Clear the one-shot state cookie so it can't be replayed.
  res.cookies.set(STATE_COOKIE, "", { path: "/", maxAge: 0 });
  return res;
}

export async function GET(req: Request) {
  const url = new URL(req.url);
  const origin = (process.env.NEXTAUTH_URL ?? url.origin).replace(/\/$/, "");

  // Recover the original target from the state cookie *before* any error path.
  const cookieState =
    req.headers
      .get("cookie")
      ?.split(";")
      .map((c) => c.trim())
      .find((c) => c.startsWith(`${STATE_COOKIE}=`))
      ?.split("=")[1] ?? "";
  const [storedState, storedReturnTo = "/"] =
    decodeURIComponent(cookieState).split(":");

  if (!isGithubOauthConfigured()) {
    return bounce(origin, storedReturnTo, {
      gh_error: "GitHub OAuth not configured",
    });
  }

  const session = await authSession();
  if (!session?.user?.email) {
    return bounce(origin, "/signin", { gh_error: "Sign in first" });
  }

  const code = url.searchParams.get("code");
  const state = url.searchParams.get("state");
  const errParam =
    url.searchParams.get("error_description") ?? url.searchParams.get("error");

  if (errParam) {
    return bounce(origin, storedReturnTo, { gh_error: errParam });
  }
  if (!code || !state) {
    return bounce(origin, storedReturnTo, {
      gh_error: "Missing code or state",
    });
  }
  if (!storedState || storedState !== state) {
    return bounce(origin, storedReturnTo, {
      gh_error: "State mismatch (try again)",
    });
  }

  try {
    const callbackUrl = `${origin}/api/integrations/github/callback`;
    const tok = await exchangeCodeForToken(code, callbackUrl);
    const me = await getAuthenticatedUser(tok.accessToken);
    await persistGithubIntegration(
      session.user.email,
      me.login,
      tok.accessToken,
      tok.scope,
    );
    return bounce(origin, storedReturnTo, { gh_connected: me.login });
  } catch (err) {
    const msg = err instanceof Error ? err.message : "GitHub connect failed";
    console.error("[github callback]", err);
    return bounce(origin, storedReturnTo, { gh_error: msg });
  }
}
