import { NextFunction, Request, Response } from "express";
import RequestError, { HttpStatusCode } from "../../helper/request_error.js";
import Client from "../../models/client.js";
import { validateJWT } from "../../keys.js";
import User from "../../models/user.js";
import { OAuthJWT } from "../../helper/jwt.js";
import Logging from "@hibas123/nodelogging";

export function GetClientAuthMiddleware(
   checksecret = true,
   internal = false,
   checksecret_if_available = false
) {
   return async (req: Request, res: Response, next: NextFunction) => {
      try {
         let client_id = req.query.client_id || req.body.client_id;
         let client_secret = req.query.client_secret || req.body.client_secret;

         if (!client_id && !client_secret && req.headers.authorization) {
            let header = req.headers.authorization;
            let [type, val] = header.split(" ");
            if (val) {
               let str = Buffer.from(val, "base64").toString("utf-8");
               let [id, secret] = str.split(":");
               client_id = id;
               client_secret = secret;
            }
         }

         if (!client_id || (!client_secret && checksecret)) {
            throw new RequestError(
               "No client credentials",
               HttpStatusCode.BAD_REQUEST
            );
         }
         let w = { client_id: client_id, client_secret: client_secret };
         if (!checksecret && !(checksecret_if_available && client_secret))
            delete w.client_secret;

         let client = await Client.findOne(w);

         if (!client) {
            throw new RequestError(
               "Invalid client_id" + (checksecret ? "or client_secret" : ""),
               HttpStatusCode.BAD_REQUEST
            );
         }

         if (internal && !client.internal) {
            throw new RequestError(
               req.__("Client has no permission for access"),
               HttpStatusCode.FORBIDDEN
            );
         }
         req.client = client;
         next();
      } catch (e) {
         if (next) next(e);
         else throw e;
      }
   };
}

export const ClientAuthMiddleware = GetClientAuthMiddleware();

export function GetClientApiAuthMiddleware(permissions?: string[]) {
   return async (req: Request, res: Response, next: NextFunction) => {
      try {
         const invalid_err = new RequestError(
            req.__("Unauthorized"),
            HttpStatusCode.UNAUTHORIZED
         );
         let token =
            (req.query.access_token as string) ||
            (req.headers.authorization as string);
         if (!token) {
            Logging.debug("No token found. Searched in query (access_token) and header (authorization)");
            throw invalid_err;
         }

         if (token.toLowerCase().startsWith("bearer "))
            token = token.substring(7);

         let data: OAuthJWT;
         try {
            data = await validateJWT(token);
         } catch (err) {
            Logging.debug("Invalid JWT", err.message);
            throw invalid_err;
         }

         let user = await User.findOne({ uid: data.user });

         if (!user) {
            Logging.debug("User not found");
            throw invalid_err;
         }

         let client = await Client.findOne({ client_id: data.application });
         if (!client) {
            Logging.debug("Client not found");
            throw invalid_err;
         }

         if (
            permissions &&
            (!data.permissions ||
               !permissions.every((e) => data.permissions.indexOf(e) >= 0))
         ) {
            Logging.debug("Invalid permissions");
            throw invalid_err;
         }

         req.user = user;
         req.client = client;
         next();
      } catch (e) {
         if (next) next(e);
         else throw e;
      }
   };
}
