import { Sequelize, WhereOptions, FindOptions, literal, fn, col, Transaction, Op, QueryTypes } from "sequelize";
import Models, { sequelize } from "../models";
import { AppError } from "../../utils/errors";
import _ from 'lodash';
import { TOKEN } from "../config/constants";

export class TokenDao {
    private accountId: number | null;
    private userId: number | null;
    private language: string;
    private scope: string[] | null;
    private config: userConfig | null;
    constructor(
        private options: {
            language: string;
            scope: string[] | null;
            accountId: number | null;
            userId: number | null;
            config: userConfig | null;
        } = {
                language: process.env.DEFAULT_LANGUAGE_CODE!,
                scope: null,
                config: null,
                userId: null,
                accountId: null,
            }
    ) {
        this.language = options.language;
        this.scope = options.scope ?? [];
        this.config = options.config ?? null;
        this.userId = options.userId ?? null
        this.accountId = options.accountId ?? null
    }
    // save token to Databases
    create = async (tokenRequestObject: TokenObject): Promise<void> => {
        try {
            // Deactivate all previously generated token for same request
            await Models.Token.update({ status: 0 }, { where: { entityValue: tokenRequestObject.entityValue, type: tokenRequestObject.type } });
            await Models.Token.create(tokenRequestObject);
            return;
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_WITH_DAO', err);
        }
    }

    // Mark token as user
    markAsUsed = async (token: Text): Promise<void> => {
        try {
            await Models.Token.update({ status: 0 }, { where: { token: token } });
            return;
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_WITH_DAO', err);
        }
    }

    // Mark token as user
    recordAttempt = async (token: Text): Promise<void> => {
        try {
            const [result, metadata] = await sequelize.query(
                `UPDATE tokens SET verifications_attempts = verifications_attempts + 1,
                status = CASE WHEN verifications_attempts + 1 > :allowedAttempts 
                    THEN 0
                    ELSE status
                END
                WHERE token = :token`,
                {
                    replacements: { allowedAttempts: +process.env.ALLOWED_VERIFICATION_ATTEMPTS!, token: token },
                    type: QueryTypes.UPDATE,
                }
            );
            return;
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_WITH_DAO', err);
        }
    }

    isActive = async (token: Text): Promise<boolean> => {
        try {
            let isTokenActive = await Models.Token.findOne({ attributes: ['id'], where: { token: token, status: TOKEN.STATUS.ACTIVE } });
            if (isTokenActive) {
                return true;
            } else {
                return false;
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_WITH_DAO', err);
        }
    }
}