import Hapi, { Request } from '@hapi/hapi'
import * as crypto from "crypto-js";
import Jwt, { decode, JwtPayload } from "jsonwebtoken";
import { AppError } from "./errors"
import { SessionService } from "../api/services/session.service"

export class Token {
    private static decrypt = (text: string) => {
        try {
            if (text) {
                let decrypted = crypto.AES.decrypt(text, process.env.CRYPTO_KEY ? process.env.CRYPTO_KEY : "NO_KEY").toString(crypto.enc.Utf8)
                return decrypted;
            } else {
                return ""
            }
        } catch (err) {
            return "";
        }
    };

    private static encrypt = (text: string) => {
        let encrypted = crypto.AES.encrypt(text, process.env.CRYPTO_KEY!).toString();
        return encrypted;
    }

    static validateToken = async (token: tokenData, type: string | string[] | Request) => {
        try {
            if (!token) return { isValid: false };
            const decrypted = this.decrypt(token.data);
            const fetchToken = JSON.parse(decrypted);
            const tokenType = typeof type === 'string' ? type : 'authorizationToken';
            if (fetchToken.type !== tokenType) {
                return { isValid: false };
            }
            if (typeof (type) == 'object') {
                let requestObj = type as unknown as Request;
                let accountToUse = ('account' in requestObj.headers) ? requestObj.headers.account : process.env.DEFAULT_ACCOUNT_KEY!
                if (fetchToken.accountKey != accountToUse) {
                    return { isValid: false };
                }
                const token = requestObj.headers.authorization ? requestObj.headers.authorization.split(" ").pop() ?? "" : "";
                let tokenText = token as unknown as Text;
                let sessionValidation = await SessionService.verifySession(requestObj, fetchToken.accountId, fetchToken.userId, tokenText);
                if (!sessionValidation) {
                    return { isValid: false };
                }
            }
            const nowMs = Date.now();
            const nowSec = Math.floor(nowMs / 1000);
            if (token.exp && token.exp <= nowSec) {
                return { isValid: false };
            }
            return {
                isValid: true,
                credentials: {
                    userData: fetchToken,
                    scope: fetchToken.permissions || []
                }
            }
        } catch (err) {
            return { isValid: false };
        }
    };

    static signToken = (type: string, tokenData: object) => {
        try {
            let expirationTime: string | null;
            switch (type) {
                case 'signup':
                    expirationTime = '30m';
                    break;
                case 'authorizationToken':
                    expirationTime = '1d';
                    break;
                case 'mobile-otp':
                    expirationTime = '5m';
                    break;
                case '2faVerification':
                    expirationTime = '5m';
                    break;
                case '2faAuthentication':
                    expirationTime = '5m';
                    break;
                case 'refreshToken':
                    expirationTime = '30d';
                    break;
                case 'chnageEmail':
                    expirationTime = '5m';
                    break;
                default:
                    expirationTime = '6h';

            }
            let life = {};
            if (expirationTime != null) {
                life = { expiresIn: expirationTime };
            }
            tokenData = { ...tokenData, ...{ type: type } }
            let data = Jwt.sign({ data: this.encrypt(JSON.stringify(tokenData)) }, process.env.JWT_PRIVATE_KEY!, life) as unknown as Text;
            return data
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', { token: "SIGN_TOKEN_PROCESS_FAILED" });
        }
    }

    static getTokenData = async (token: Text, type: string) => {
        try {
            let tokenInfo = decode(token as unknown as string);
            if (tokenInfo) {
                const tokenData = await this.validateToken(tokenInfo as unknown as tokenData, type);
                if (tokenData && tokenData.isValid) {
                    return tokenData.credentials?.userData;
                } else {
                    throw new AppError(400, 'INVALID_TOKEN', { token: "INVALID_TOKEN" });
                }
            } else {
                throw new AppError(400, 'INVALID_TOKEN', { token: "INVALID_TOKEN" });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', { token: "SIGN_TOKEN_PROCESS_FAILED" });
        }
    }
}