import { DiscordLinkRequest, RegisterToken, User } from "@autoplex/database";
import { RouteRegisterFactory, handleRequest, MiddlewareMethod, Status, respond } from "@autoplex/webserver";
import jwt from "jsonwebtoken"
import Application from "../../../Application";
import { LoginRequest, ILoginFormBody } from "../requests";
import { RegisterRequest, IRegisterFormBody } from "../requests";
import { LinkDiscordRequest } from "../requests";
import { auth } from "../middleware/auth";

/**
 * Register authentication routes
 */
export default function register(factory: RouteRegisterFactory<MiddlewareMethod<void>, Application>, app: Application) {

	/**
	 * Verify the auth token
	 */
	factory.get("/auth/verify", [auth], (request, reply) => {
		respond(reply, Status.Ok);
	});

	// Login ---------------------------------------------------------------------------------------

	factory.post("/auth/login", handleRequest([LoginRequest], async (request, reply) => {
		let form = <ILoginFormBody>request.body;
		let user = await User.authenticate(form.email, form.password);
		if (user === null) {
			return respond(reply, Status.Unauthorized);
		}
		let body = { id: user.id, name: user.name, isAdmin: user.isAdmin };
		let token = jwt.sign(body, app.APP_KEY, { expiresIn: 60*60*24 });
		// Below code requires SSH to store cookies securely
		// Store the header/payload in the client, store the signature in a secure httpOnly cookie
		// if ((<any>request.query)["use_cookies"] || (<any>request.query)["use_cookies"] === undefined) {
		// 	let [header, payload, signature] = token.split('.');
		// 	token = `${header}.${payload}`;
		// 	reply.setCookie("jwt_signature", signature, {
		// 		path: '/',
		// 		httpOnly: true,
		// 		sameSite: true,
		// 		secure: true
		// 	});
		// }
		respond(reply, Status.Ok, { token });
	}));

	// Registration --------------------------------------------------------------------------------

	/**
	 * Register a user
	 */
	factory.post("/auth/register", handleRequest([RegisterRequest], async (request, reply) => {
		let body = <IRegisterFormBody>request.body;
		await User.createUser(
			body.name.trim(),
			body.email.trim(),
			body.password.trim()
		);
		await RegisterToken.delete({token: body.token });
		respond(reply, Status.Ok);
	}));

	/**
	 * Validate a registration token
	 */
	factory.post("/auth/register/validate_token/:token", async (request, reply) => {
		let token: string = (<any>request.params)["token"];
		if (!(await RegisterToken.isValid(token))) {
			return respond(reply, Status.UnprocessableEntity);
		}
		respond(reply, Status.Ok);
	});

	/**
	 * Check if an email address is available (assumes it's correct)
	 */
	factory.post("/auth/register/available_email/:email", async (request, reply) => {
		let email: string = (<any>request.params)["email"].trim();
		if (!Boolean(email) || await User.count({email}) != 0) {
			return respond(reply, Status.UnprocessableEntity);
		}
		respond(reply, Status.Ok);
	});

	// Account Linking -----------------------------------------------------------------------------

	factory.post("/auth/link/verify/discord/:token", async (request, reply) => {
		let token = <string>(<any>request.params)["token"];
		if (await DiscordLinkRequest.count({ where: { token } }) == 0) {
			return respond(reply, Status.NotFound);
		}
		return respond(reply, Status.Ok);
	});

	/**
	 * Link a Discord account
	 */
	factory.post("/auth/link/discord/:token",  handleRequest([LinkDiscordRequest], async (request, reply) => {
		// Fetch the link request from the token
		let token = <string|undefined>(<any>request.params)["token"];
		let linkRequest = await DiscordLinkRequest.findOne({ where: { token } });
		if (linkRequest === undefined) {
			return respond(reply, Status.NotFound);
		}
		// // Validate the credentials
		let form = <ILoginFormBody>request.body;
		let user = await User.authenticate(form.email, form.password);
		if (user === null) {
			return respond(reply, Status.Unauthorized);
		}
		// Link the accounts
		await linkRequest.linkUser(user);
		return respond(reply, Status.Ok);
	}));
}
