import { v4 } from 'uuid';
import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js";
import {BaseController} from "./BaseController";
import {adminApi} from "../Services/AdminApi";
import {jwtTokenManager} from "../Services/JWTTokenManager";
import {parse} from "query-string";

export interface TokenInterface {
    userUuid: string
}

export class AuthenticateController extends BaseController {

    constructor(private App : TemplatedApp) {
        super();
        this.register();
        this.verify();
        this.anonymLogin();
    }

    //Try to login with an admin token
    private register(){
        this.App.options("/register", (res: HttpResponse, req: HttpRequest) => {
            this.addCorsHeaders(res);

            res.end();
        });

        this.App.post("/register", (res: HttpResponse, req: HttpRequest) => {
            (async () => {
                res.onAborted(() => {
                    console.warn('Login request was aborted');
                })
                const param = await res.json();

                //todo: what to do if the organizationMemberToken is already used?
                const organizationMemberToken:string|null = param.organizationMemberToken;

                try {
                    if (typeof organizationMemberToken != 'string') throw new Error('No organization token');
                    const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
                    const userUuid = data.userUuid;
                    const organizationSlug = data.organizationSlug;
                    const worldSlug = data.worldSlug;
                    const roomSlug = data.roomSlug;
                    const mapUrlStart = data.mapUrlStart;
                    const textures = data.textures;

                    const authToken = jwtTokenManager.createJWTToken(userUuid);
                    res.writeStatus("200 OK");
                    this.addCorsHeaders(res);
                    res.end(JSON.stringify({
                        authToken,
                        userUuid,
                        organizationSlug,
                        worldSlug,
                        roomSlug,
                        mapUrlStart,
                        textures
                    }));

                } catch (e) {
                    console.error("An error happened", e)
                    res.writeStatus(e.status || "500 Internal Server Error");
                    this.addCorsHeaders(res);
                    res.end('An error happened');
                }


            })();
        });

    }

    private verify(){
        this.App.options("/verify", (res: HttpResponse, req: HttpRequest) => {
            this.addCorsHeaders(res);

            res.end();
        });

        this.App.get("/verify", (res: HttpResponse, req: HttpRequest) => {
            (async () => {
                const query = parse(req.getQuery());

                res.onAborted(() => {
                    console.warn('verify request was aborted');
                })

                try {
                    await jwtTokenManager.getUserUuidFromToken(query.token as string);
                } catch (e) {
                    res.writeStatus("400 Bad Request");
                    this.addCorsHeaders(res);
                    res.end(JSON.stringify({
                        "success": false,
                        "message": "Invalid JWT token"
                    }));
                    return;
                }
                res.writeStatus("200 OK");
                this.addCorsHeaders(res);
                res.end(JSON.stringify({
                    "success": true
                }));
            })();
        });

    }

    //permit to login on application. Return token to connect on Websocket IO.
    private anonymLogin(){
        this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => {
            this.addCorsHeaders(res);

            res.end();
        });

        this.App.post("/anonymLogin", (res: HttpResponse, req: HttpRequest) => {

            res.onAborted(() => {
                console.warn('Login request was aborted');
            })

            const userUuid = v4();
            const authToken = jwtTokenManager.createJWTToken(userUuid);
            res.writeStatus("200 OK");
            this.addCorsHeaders(res);
            res.end(JSON.stringify({
                authToken,
                userUuid,
            }));
        });
    }
}
