import { Handlers } from "$fresh/server.ts";
import { create, getNumericDate } from "https://deno.land/x/djwt@v2.2/mod.ts";
import { oauth2Client } from "@lib/auth.ts";
import { getCookies, setCookie } from "$std/http/cookie.ts";
import { codeChallengeMap } from "./login.ts";
import { GITEA_SERVER, JWT_SECRET, SESSION_DURATION } from "@lib/env.ts";
import { userDB } from "@lib/db.ts";
import { GiteaOauthUser } from "@lib/types.ts";
import { BadRequestError } from "@lib/errors.ts";

export const handler: Handlers = {
  async GET(request) {
    if (!JWT_SECRET) {
      throw new BadRequestError();
    }

    // Exchange the authorization code for an access token
    const cookies = getCookies(request.headers);

    const stored = codeChallengeMap.get(cookies["code_challenge"]);
    if (!stored) {
      throw new BadRequestError();
    }

    const { codeVerifier, redirect } = stored;

    const tokens = await oauth2Client.code.getToken(request.url, {
      codeVerifier,
    });

    // Use the access token to make an authenticated API request
    const userInfo = `${GITEA_SERVER}/login/oauth/userinfo`;
    const userResponse = await fetch(userInfo, {
      headers: {
        Authorization: `token ${tokens.accessToken}`,
      },
    });

    const oauthUser = await userResponse.json() as GiteaOauthUser;

    const allUsers = await userDB.findAll();
    let user = allUsers.find((u) => u.name === oauthUser.name);

    if (!user) {
      user = await userDB.create({
        createdAt: new Date(),
        email: oauthUser.email,
        name: oauthUser.name,
      });
    }

    const jwt = await create({ alg: "HS512", type: "JWT" }, {
      id: user.id,
      name: user.name,
      exp: getNumericDate(SESSION_DURATION),
    }, JWT_SECRET);

    const headers = new Headers({
      location: redirect || "/",
    });

    setCookie(headers, {
      name: "session_cookie",
      value: jwt,
      path: "/",
      maxAge: SESSION_DURATION,
      httpOnly: false,
      secure: true,
      sameSite: "Lax",
    });

    return new Response(null, {
      headers,
      status: 302,
    });
  },
};
