import { getReasonPhrase, StatusCodes } from 'http-status-codes';
import * as jwt                         from 'jsonwebtoken';
import { JwtPayload }                   from 'jsonwebtoken';
import Config                           from '../config/Config';
import express                          from 'express';
import UserManager                      from '../managers/UserManager';
import { User }                         from '../types/DatabaseTypes';
import DojoBackendResponse              from '../shared/types/Dojo/DojoBackendResponse';


class Session {
    private _profile!: User;

    get profile(): User {
        return this._profile;
    }

    set profile(newProfile: User) {
        delete newProfile.password;
        this._profile = newProfile;
    }

    constructor() { }

    async initSession(req: express.Request, res: express.Response) {
        const authorization = req.headers.authorization;
        if ( authorization ) {
            if ( authorization.startsWith('Bearer ') ) {
                const jwtToken = authorization.replace('Bearer ', '');

                try {
                    const jwtData = jwt.verify(jwtToken, Config.jwtConfig.secret) as JwtPayload;

                    if ( jwtData.profile ) {
                        this.profile = jwtData.profile;
                        this.profile = await UserManager.getById(this.profile.id!) ?? this.profile;
                    }
                } catch ( err ) {
                    res.sendStatus(StatusCodes.UNAUTHORIZED).end();
                }
            }
        }
    }

    private static getToken(profileJson: any): string | null {
        return profileJson === null ? null : jwt.sign({ profile: profileJson }, Config.jwtConfig.secret, Config.jwtConfig.expiresIn > 0 ? { expiresIn: Config.jwtConfig.expiresIn } : {});
    }

    private async getResponse<T>(code: number, data: T, descriptionOverride?: string): Promise<DojoBackendResponse<T>> {
        const profileJson = this.profile;

        let reasonPhrase = '';

        try {
            reasonPhrase = getReasonPhrase(code);
        } catch {}

        return {
            timestamp   : (new Date()).toISOString(),
            code        : code,
            description : descriptionOverride ? descriptionOverride : reasonPhrase,
            sessionToken: Session.getToken(profileJson),
            data        : data
        };
    }

    /*
     Send a response to the client
     Information: Data could be a promise or an object. If it's a promise, we wait on the data to be resolved before sending the response
     */
    sendResponse(res: express.Response, code: number, data?: any, descriptionOverride?: string, internalCode?: number) {
        Promise.resolve(data).then((toReturn: any) => {
            this.getResponse(internalCode ?? code, toReturn, descriptionOverride).then(response => {
                res.status(code).json(response);
            });
        });
    }
}


export default Session;