import { DbInstance, SavePriority } from "../instance";
import consoleFactory from '@lib/console';
import { DatabasePlayerType, DatabaseWhitelistApprovalsType, DatabaseWhitelistRequestsType } from '../databaseTypes';
import { now } from '@lib/misc';
const console = consoleFactory('DatabaseDao');


/**
 * Data access object for cleaning up the database.
 */
export default class CleanupDao {
    constructor(private readonly db: DbInstance) { }

    private get dbo() {
        if (!this.db.obj || !this.db.isReady) throw new Error(`database not ready yet`);
        return this.db.obj;
    }

    private get chain() {
        if (!this.db.obj || !this.db.isReady) throw new Error(`database not ready yet`);
        return this.db.obj.chain;
    }


    /**
     * Cleans the database by removing every entry that matches the provided filter function.
     * @returns {number} number of removed items
     */
    bulkRemove(
        tableName: 'players' | 'actions' | 'whitelistApprovals' | 'whitelistRequests',
        filterFunc: Function
    ): number {
        if (!Array.isArray(this.dbo.data[tableName])) throw new Error('Table selected isn\'t an array.');
        if (typeof filterFunc !== 'function') throw new Error('filterFunc must be a function.');

        try {
            this.db.writeFlag(SavePriority.HIGH);
            const removed = this.chain.get(tableName)
                .remove(filterFunc as any)
                .value();
            return removed.length;
        } catch (error) {
            const msg = `Failed to clean database with error: ${(error as Error).message}`;
            console.verbose.error(msg);
            throw new Error(msg);
        }
    }


    /**
     * Cleans the hwids from the database.
     * @returns {number} number of removed HWIDs
     */
    wipeHwids(
        fromPlayers: boolean,
        fromBans: boolean,
    ): number {
        if (!Array.isArray(this.dbo.data.players)) throw new Error('Players table isn\'t an array yet.');
        if (!Array.isArray(this.dbo.data.players)) throw new Error('Actions table isn\'t an array yet.');
        if (typeof fromPlayers !== 'boolean' || typeof fromBans !== 'boolean') throw new Error('The parameters should be booleans.');

        try {
            this.db.writeFlag(SavePriority.HIGH);
            let removed = 0;
            if (fromPlayers) {
                this.chain.get('players')
                    .map(player => {
                        removed += player.hwids.length;
                        player.hwids = [];
                        return player;
                    })
                    .value();
            }
            if (fromBans)
                this.chain.get('actions')
                    .map(action => {
                        if (action.type !== 'ban' || !action.hwids) {
                            return action;
                        } else {
                            removed += action.hwids.length;
                            action.hwids = [];
                            return action;
                        }
                    })
                    .value();
            return removed;
        } catch (error) {
            const msg = `Failed to clean database with error: ${(error as Error).message}`;
            console.verbose.error(msg);
            throw new Error(msg);
        }
    }


    /**
     * Cron func to optimize the database removing players and whitelist reqs/approvals
     */
    runDailyOptimizer() {
        const oneDay = 24 * 60 * 60;

        //Optimize players
        //Players that have not joined the last 16 days, and have less than 2 hours of playtime
        let playerRemoved;
        try {
            const sixteenDaysAgo = now() - (16 * oneDay);
            const filter = (p: DatabasePlayerType) => {
                return (p.tsLastConnection < sixteenDaysAgo && p.playTime < 120);
            }
            playerRemoved = this.bulkRemove('players', filter);
        } catch (error) {
            const msg = `Failed to optimize players database with error: ${(error as Error).message}`;
            console.error(msg);
        }

        //Optimize whitelistRequests + whitelistApprovals
        //Removing the ones older than 7 days
        let wlRequestsRemoved, wlApprovalsRemoved;
        const sevenDaysAgo = now() - (7 * oneDay);
        try {
            const wlRequestsFilter = (req: DatabaseWhitelistRequestsType) => {
                return (req.tsLastAttempt < sevenDaysAgo);
            }
            wlRequestsRemoved = txCore.database.whitelist.removeManyRequests(wlRequestsFilter).length;

            const wlApprovalsFilter = (req: DatabaseWhitelistApprovalsType) => {
                return (req.tsApproved < sevenDaysAgo);
            }
            wlApprovalsRemoved = txCore.database.whitelist.removeManyApprovals(wlApprovalsFilter).length;
        } catch (error) {
            const msg = `Failed to optimize players database with error: ${(error as Error).message}`;
            console.error(msg);
        }

        this.db.writeFlag(SavePriority.LOW);
        console.ok(`Internal Database optimized. This applies only for the txAdmin internal database, and does not affect your MySQL or framework (ESX/QBCore/etc) databases.`);
        console.ok(`- ${playerRemoved} players that haven't connected in the past 16 days and had less than 2 hours of playtime.`);
        console.ok(`- ${wlRequestsRemoved} whitelist requests older than a week.`);
        console.ok(`- ${wlApprovalsRemoved} whitelist approvals older than a week.`);
    }
}
