import evaluateCheck, { getRequiredNumberOfDice } from "./check-evaluation";

interface DS4CheckTermData extends DiceTerm.TermData {
    canFumble: boolean;
}

/**
 * Implements DS4 Checks as an emulated "dice throw".
 *
 * @example
 * - Roll a check against a Check Target Number (CTN) of 18: `/r dsv18`
 * - Roll a check with multiple dice against a CTN of 34: `/r dsv34`
 * - Roll a check with a racial ability that makes `2` a coup and `19` a fumble: `/r dsv19c2,19`
 * - Roll a check with a racial ability that makes `5` a coup and default fumble: `/r dsv19c5`
 */
export class DS4Check extends DiceTerm {
    constructor({ modifiers = [], options = {}, canFumble = true }: Partial<DS4CheckTermData> = {}) {
        super({
            faces: 20,
            modifiers: modifiers,
            options: options,
        });

        this.canFumble = canFumble;

        // Parse and store check target number
        const targetValueModifier = this.modifiers.filter((m) => m[0] === "v")[0];
        const tvRgx = new RegExp("v([0-9]+)?");
        const tvMatch = targetValueModifier?.match(tvRgx);
        if (tvMatch) {
            const [parseTargetValue] = tvMatch.slice(1);
            this.checkTargetNumber = parseTargetValue
                ? parseInt(parseTargetValue)
                : DS4Check.DEFAULT_CHECK_TARGET_NUMBER;
        }

        this.number = getRequiredNumberOfDice(this.checkTargetNumber);

        // Parse and store minimumCoupResult and maximumFumbleResult
        const coupFumbleModifier = this.modifiers.filter((m) => m[0] === "c")[0];
        const cfmRgx = new RegExp("c([0-9]+)?,([0-9]+)?");
        const cfmMatch = coupFumbleModifier?.match(cfmRgx);
        if (cfmMatch) {
            const [parseMaximumCoupResult, parseMinimumFumbleResult] = cfmMatch.slice(1);
            this.maximumCoupResult = parseMaximumCoupResult
                ? parseInt(parseMaximumCoupResult)
                : DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;
            this.minimumFumbleResult = parseMinimumFumbleResult
                ? parseInt(parseMinimumFumbleResult)
                : DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
            if (this.minimumFumbleResult <= this.maximumCoupResult)
                throw new SyntaxError(game.i18n.localize("DS4.ErrorDiceCritOverlap"));
        }
    }

    coup: boolean | null = null;
    fumble: boolean | null = null;
    canFumble: boolean;
    checkTargetNumber = DS4Check.DEFAULT_CHECK_TARGET_NUMBER;
    minimumFumbleResult = DS4Check.DEFAULT_MINIMUM_FUMBLE_RESULT;
    maximumCoupResult = DS4Check.DEFAULT_MAXIMUM_COUP_RESULT;

    /**
     * @override
     */
    get expression(): string {
        return `ds${this.modifiers.join("")}`;
    }

    /**
     * @override
     */
    get total(): number | null {
        if (this.fumble) return 0;
        return super.total;
    }

    /**
     * @override
     */
    roll({ minimize = false, maximize = false } = {}): DiceTerm.Result {
        // Swap minimize / maximize because in DS4, the best possible roll is a 1 and the worst possible roll is a 20
        return super.roll({ minimize: maximize, maximize: minimize });
    }

    evaluateResults(): void {
        const dice = this.results.map((die) => die.result);
        const results = evaluateCheck(dice, this.checkTargetNumber, {
            maximumCoupResult: this.maximumCoupResult,
            minimumFumbleResult: this.minimumFumbleResult,
            canFumble: this.canFumble,
        });
        this.results = results;
        this.coup = results[0].success ?? false;
        this.fumble = results[0].failure ?? false;
    }

    // // DS4 only allows recursive explosions
    // explode(modifier: string): void {
    //     const rgx = /[xX]/;
    //     const match = modifier.match(rgx);
    //     if (!match) return;

    //     this.results = (this.results as Array<RollResult>)
    //         .map((r) => {
    //             const intermediateResults = [r];

    //             let checked = 0;
    //             while (checked < intermediateResults.length) {
    //                 const r = intermediateResults[checked];
    //                 checked++;
    //                 if (!r.active) continue;

    //                 if (r.dice[0] <= this.maxCritSuccess) {
    //                     r.exploded = true;
    //                     const newRoll = this.rollWithDifferentBorders({}, true);
    //                     intermediateResults.push(newRoll);
    //                 }

    //                 if (checked > 1000) throw new Error(game.i18n.localize("DS4.ErrorExplodingRecursionLimitExceeded"));
    //             }
    //             return intermediateResults;
    //         })
    //         .reduce((acc, cur) => {
    //             return acc.concat(cur);
    //         }, []);
    // }

    static readonly DEFAULT_CHECK_TARGET_NUMBER = 10;
    static readonly DEFAULT_MAXIMUM_COUP_RESULT = 1;
    static readonly DEFAULT_MINIMUM_FUMBLE_RESULT = 20;
    static DENOMINATION = "s";
    static MODIFIERS = {
        //x: "explode",
        c: (): void => undefined, // Modifier is consumed in constructor for crit
        v: "evaluateResults",
    };
}