import { ITokenSchema } from "@autoplex-api/request";
import { User } from "@autoplex/database";
import { IteratorNext, MiddlewareRequest, FastifyReply } from "@autoplex/webserver";
import jwt from "jsonwebtoken";
import Application from "../../../Application";

/**
 * Attempt to authenticate a client's JWT token
 */
async function authenticateJwtToken<T>(request: MiddlewareRequest<T>, reply: FastifyReply): Promise<User|undefined> {
	// Verify headers
	if (!request.headers["authorization"]) {
		reply.status(401);
		reply.send({ status: "Unauthorized" });
		return;
	}
	if (!request.headers["authorization"].startsWith("Bearer ")) {
		reply.status(400);
		reply.send({ status: "Bad request" });
		return;
	}
	// Construct the token string
	let token = request.headers["authorization"].slice(7).trim();
	if ((token.match(/\./g)||[]).length < 2) {
		token += '.' + (request.cookies.jwt_signature ?? "").trim();
	}
	// Decode the token
	let user: User;
	try {
		let decoded = <ITokenSchema>jwt.verify(token, Application.instance().APP_KEY);
		user = await User.findOneOrFail(decoded.id, { relations: [ "quota" ] });
	} catch(e) {
		reply.status(401);
		reply.send({ status: "Unauthorized" });
		return;
	}
	return user;
}

/**
 * The parameter types for the auth middleware
 */
export interface IAuthMiddlewareParams {
	auth: {
		user: User
	}
}

/**
 * Ensure that a valid authentication token is provided
 */
export async function auth<T extends IAuthMiddlewareParams>(request: MiddlewareRequest<T>, reply: FastifyReply, next: IteratorNext) {
	let user = await authenticateJwtToken(request, reply);
	if (user === undefined) {
		// The authenticateJwtToken function sends out the response
		return;
	}
	request.middlewareParams = <any><IAuthMiddlewareParams>{
		auth: { user }
	};
	next();
}
