import { NextRequest, NextResponse } from "next/server";
import {
  GITHUB_OAUTH_STATE_COOKIE,
  VIEWER_SESSION_COOKIE,
  VIEWER_SESSION_TTL_MS,
} from "@shared/auth/constants";
import { createViewerSessionToken } from "@shared/auth/session";
import { resolveGithubOAuthConfig, toGithubViewer } from "@/lib/auth/github";
import { getAppOrigin, isSecureRequest } from "@/lib/auth/request-context";

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

interface GithubTokenResponse {
  access_token?: string;
  error?: string;
  error_description?: string;
}

interface GithubProfileResponse {
  id: number;
  login: string;
  name: string | null;
  avatar_url: string | null;
}

export async function GET(request: NextRequest): Promise<NextResponse> {
  const appOrigin = getAppOrigin(request);
  const oauthConfig = resolveGithubOAuthConfig({
    baseUrl: appOrigin,
    clientId: process.env.GITHUB_CLIENT_ID,
    clientSecret: process.env.GITHUB_CLIENT_SECRET,
    authSecret: process.env.AUTH_SESSION_SECRET,
    configuredCallbackUrl: process.env.GITHUB_OAUTH_CALLBACK_URL,
    requireCallbackMatch: process.env.GITHUB_OAUTH_REQUIRE_CALLBACK_MATCH === "true",
  });
  if (!oauthConfig.configured) {
    return NextResponse.json(
      {
        error: "GitHub OAuth is not available for this origin",
        code: oauthConfig.reason,
      },
      { status: 503 }
    );
  }

  const code = request.nextUrl.searchParams.get("code");
  const state = request.nextUrl.searchParams.get("state");
  const expectedState = request.cookies.get(GITHUB_OAUTH_STATE_COOKIE)?.value;

  if (!code || !state || !expectedState || state !== expectedState) {
    return NextResponse.redirect(new URL("/?error=github_auth_state", `${appOrigin}/`));
  }

  const tokenResponse = await fetch("https://github.com/login/oauth/access_token", {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: new URLSearchParams({
      client_id: oauthConfig.clientId,
      client_secret: oauthConfig.clientSecret,
      code,
      redirect_uri: oauthConfig.callbackUrl,
      state,
    }).toString(),
    cache: "no-store",
  });

  const tokenJson = (await tokenResponse.json()) as GithubTokenResponse;
  if (!tokenResponse.ok || !tokenJson.access_token) {
    return NextResponse.redirect(new URL("/?error=github_auth_exchange", `${appOrigin}/`));
  }

  const profileResponse = await fetch("https://api.github.com/user", {
    headers: {
      Accept: "application/vnd.github+json",
      Authorization: `Bearer ${tokenJson.access_token}`,
      "User-Agent": "sre-simulator",
    },
    cache: "no-store",
  });

  if (!profileResponse.ok) {
    return NextResponse.redirect(new URL("/?error=github_auth_profile", `${appOrigin}/`));
  }

  const profile = (await profileResponse.json()) as GithubProfileResponse;
  const viewer = toGithubViewer(profile);
  const issuedAt = Date.now();
  const sessionToken = createViewerSessionToken(
    {
      ...viewer,
      issuedAt,
      expiresAt: issuedAt + VIEWER_SESSION_TTL_MS,
    },
    oauthConfig.authSecret
  );

  const response = NextResponse.redirect(new URL("/", `${appOrigin}/`));
  response.cookies.delete(GITHUB_OAUTH_STATE_COOKIE);
  response.cookies.set({
    name: VIEWER_SESSION_COOKIE,
    value: sessionToken,
    httpOnly: true,
    sameSite: "lax",
    secure: isSecureRequest(request),
    path: "/",
    maxAge: Math.floor(VIEWER_SESSION_TTL_MS / 1000),
  });
  return response;
}
