import { GoogleUserInfo, User } from "@nirvana/core/models";
import { JwtClaims, authCheck } from "../middleware/auth";
import express, { Application, Request, Response } from "express";

import LoginResponse from "../../core/responses/login.response";
import { OAuth2Client } from "google-auth-library";
import { ObjectID } from "bson";
import { ObjectId } from "mongodb";
import UserDetailsResponse from "../../core/responses/userDetails.response";
import { UserService } from "../services/user.service";
import { UserStatus } from "../../core/models/user.model";
import { collections } from "../services/database.service";
import { loadConfig } from "../config";

const jwt = require("jsonwebtoken");

const config = loadConfig();

const client = new OAuth2Client(config.GOOGLE_AUTH_CLIENT_ID);

export default function getUserRoutes() {
  const router = express.Router();

  router.use(express.json());

  // get user details based on id token
  router.get("/", authCheck, getUserDetails);

  router.get("/login", login);

  router.get("/authcheck", authCheck, handleAuthCheck);

  return router;
}

async function handleAuthCheck(req: Request, res: Response) {
  try {
    res.status(200).json("You are good to go!");
  } catch (error) {
    res.status(401).json("Unauthorized");
  }
}

async function getUserDetails(req: Request, res: Response) {
  try {
    const userInfo = res.locals.userInfo as JwtClaims;

    const user = await UserService.getUserById(userInfo.userId);

    user
      ? res.status(200).json(new UserDetailsResponse(user))
      : res.status(404).json("No such user");
  } catch (error) {
    console.log(error);
    res.status(500).json(`Problem with signing user up or logging in`);
  }
}

/** Create user if doesn't exist
 *  Returns jwt token for client and user details
 */
async function login(req: Request, res: Response) {
  // passed in accesstoken no matter what
  const { access_token, id_token } = req.query;

  try {
    const ticket = await client.verifyIdToken({
      idToken: (id_token as string) ?? "",
      audience: config.GOOGLE_AUTH_CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
    });
    const googleUserId = ticket.getPayload()?.sub as string;
    const email = ticket.getPayload()?.email as string;

    if (!googleUserId || !email) {
      res.status(401).json("no google account found");
      return;
    }

    // return user details if it passed auth middleware
    let user = await UserService.getUserByEmail(email);

    // if no user found, then go ahead and create user
    if (!user) {
      if (!access_token) {
        res.status(400).json("No access token provided");
        return;
      }

      // get google user info from access token
      const userInfo: GoogleUserInfo =
        await UserService.getGoogleUserInfoWithAccessToken(
          access_token as string
        );

      // create initial user model object
      const newUser = new User(
        googleUserId,
        userInfo.email,
        userInfo.verifiedEmail,
        userInfo.name,
        userInfo.given_name,
        userInfo.family_name,
        userInfo.picture,
        userInfo.locale,
        new Date(),
        UserStatus.ONLINE,
        new Date()
      );

      // create user if not exists
      const insertResult = await UserService.createUserIfNotExists(newUser);

      newUser._id = insertResult?.insertedId;

      // create jwt token with new user info
      const jwtToken = jwt.sign(
        {
          userId: newUser._id,
          googleUserId: newUser.googleId,
          picture: newUser.picture,
          email: newUser.email,
          name: newUser.name,
        },
        config.JWT_TOKEN_SECRET
      );

      insertResult
        ? res.status(200).json(new LoginResponse(jwtToken, newUser))
        : res.status(500).json("Failed to create account, already exists");

      return;
    }

    // create jwt token with new user info
    const jwtToken = jwt.sign(
      {
        userId: user._id,
        googleUserId: user.googleId,
        picture: user.picture,
        email: user.email,
        name: user.name,
      },
      config.JWT_TOKEN_SECRET
    );

    res.status(200).json(new LoginResponse(jwtToken, user));
  } catch (error) {
    console.log(error);
    res.status(500).json(`Problem with signing user up or logging in`);
  }
}
