import jwt from 'jsonwebtoken';
import UserDatabaseService, {
    IUserListService,
} from '../../services/db/userList';
import EnvVariableHelper from '../envVariableHelper';
import { UserTokenType } from '../../declarations/validations';

export default class TokenMgmt {
    private static ACCESS_TOKEN_SECRET: string =
        EnvVariableHelper.VARIABLES.ACCESS_TOKEN_SECRET;

    public static accessTokenValidityPeriodInSeconds = 1800;

    public static refreshTokenValidityPeriodInDays = 30;

    private static accessTokenOptions = {
        expiresIn: `${TokenMgmt.accessTokenValidityPeriodInSeconds}s`,
    };

    private static REFRESH_TOKEN_SECRET: string =
        EnvVariableHelper.VARIABLES.REFRESH_TOKEN_SECRET;

    private static refreshTokenOptions = {
        expiresIn: `${TokenMgmt.refreshTokenValidityPeriodInDays} days`,
    };

    public static userListService: IUserListService = new UserDatabaseService();

    static generateAccessToken(user: UserTokenType) {
        if (!user) {
            return '';
        }
        const payload = {
            ...user,
        };
        return jwt.sign(
            payload,
            this.ACCESS_TOKEN_SECRET,
            this.accessTokenOptions
        );
    }

    static generateRefreshToken(user: UserTokenType) {
        if (!user) {
            return '';
        }
        const payload = {
            ...user,
        };
        return jwt.sign(
            payload,
            this.REFRESH_TOKEN_SECRET,
            this.refreshTokenOptions
        );
    }

    static validateAccessToken(accessToken: string) {
        try {
            const decoded = jwt.verify(accessToken, this.ACCESS_TOKEN_SECRET);
            return {
                success: 'JWT Access Token successfully validated',
                user: decoded,
            };
        } catch (e) {
            return { error: 'JWT Access Token Validation failed', reason: e };
        }
    }

    static async refreshAccessToken(refreshToken: string) {
        return new Promise<string>((resolve, reject) => {
            const jwtUser: any = jwt.verify(
                refreshToken,
                this.REFRESH_TOKEN_SECRET
            );

            this.userListService
                .findSessionByToken(refreshToken)
                .then((dbSession) => {
                    if (dbSession === null) {
                        // logout or blacklisted
                        reject(new Error('User logout or blacklisted'));
                    }
                    const { iat, exp, ...userToken } = jwtUser;
                    // sign access token
                    const result = this.generateAccessToken({
                        ...userToken,
                    });
                    resolve(result);
                })
                .catch((error) => {
                    reject(error);
                });
        });
    }
}
