import { getLogger } from "@/logger";
import { cookies } from "next/dist/client/components/headers";
import {
  checkInOrg,
  getGitHubAvatar,
  getGitHubUser,
  GitHubAccessTokenResponse
} from "../oauth";
import prisma from "@/prisma";
import * as ldap from "@/ldap";
import { v4 } from "uuid";
import { AuthState, getAuthState, getUser } from "@/auth";

const logger = getLogger("/oauth/github/redirect");

export async function GET(request: Request) {
  let url = new URL(request.url);
  let code = url.searchParams.get("code");
  let state = url.searchParams.get("state");

  if (code === null || state === null) {
    logger.info("request made with missing code/state");
    return new Response("missing code/state", { status: 400 });
  }

  const cookieStore = cookies();
  let cookieState = cookieStore.get("state");
  // prevent forgery
  if (cookieState?.value !== state) {
    logger.info(
      "request made with invalid state - someone attempting forgery?"
    );
    return new Response("state is invalid", { status: 400 });
  }

  let query = new URLSearchParams();
  query.set("client_id", process.env.GITHUB_CLIENT_ID);
  query.set("client_secret", process.env.GITHUB_CLIENT_SECRET);
  query.set("code", code);
  query.set("redirect_uri", `${process.env.BASE_DOMAIN}oauth/github/redirect`);

  let tokenUrl = `https://github.com/login/oauth/access_token?${query.toString()}`;
  let tokenResponse = await fetch(tokenUrl, {
    method: "POST",
    headers: {
      Accept: "application/json"
    }
  });

  if (!tokenResponse.ok) {
    logger.error("baby");
    throw "baby";
  }

  let resp: GitHubAccessTokenResponse = await tokenResponse.json();
  let accessToken = resp.access_token;
  const githubUser = await getGitHubUser(accessToken);
  const inOrg = await checkInOrg(githubUser.login);

  if (!inOrg) {
    logger.info({ id: githubUser.login }, "user tried to sign up");
    return new Response("not permitted to register account", { status: 403 });
  }

  let userId = null;
  const authState = await getAuthState();
  if (authState === AuthState.LoggedIn) {
    const currentUser = await getUser();
    userId = currentUser?.id;
  }

  const githubAuth = await prisma.gitHubAuth.upsert({
    where: { id: githubUser.id },
    create: {
      id: githubUser.id,
      accessToken,
      user:
        userId != null
          ? { connect: { id: userId } }
          : { create: { username: null } }
    },
    update: { accessToken }
  });

  const user = await prisma.user.findFirst({
    where: {
      id: githubAuth.userId
    }
  });

  // check if user got deleted from ldap, same as /api/register
  if (
    user !== null &&
    user.username !== null &&
    !(await ldap.checkUserExists(user.username))
  ) {
    logger.warn(
      { username: user.username },
      "user doesn't exist in ldap anymore"
    );
    user.username = null;
    await prisma.user.update({
      where: {
        id: user.id
      },
      data: {
        username: null
      }
    });
  }

  const authTicket = await prisma.authTicket.upsert({
    where: {
      userId: user!.id
    },
    create: {
      userId: user!.id,
      ticket: v4(),
      expiresAt: new Date(Date.now() + 86400000)
    },
    update: {
      ticket: v4(),
      expiresAt: new Date(Date.now() + 86400000)
    }
  });

  await prisma.user.update({
    where: {
      id: user!.id
    },
    data: {
      authTicket: {
        connect: {
          id: authTicket.id
        }
      }
    }
  });

  if (user?.username !== null) {
    return new Response(null, {
      status: 302,
      headers: {
        "Set-Cookie": `ticket=${authTicket.ticket}; Path=/;`,
        Location: "/me"
      }
    });
  }

  const avatarUrl = await getGitHubAvatar(accessToken);

  const query2 = new URLSearchParams();
  query2.append("username", githubUser.login);
  query2.append("email", githubUser.email);
  query2.append("avatar", avatarUrl);

  return new Response(null, {
    status: 302,
    headers: {
      "Set-Cookie": `ticket=${authTicket.ticket}; Path=/;`,
      Location: "/register?" + query2.toString()
    }
  });
}
