import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs';

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;

export default class RerollDamageDialog extends HandlebarsApplicationMixin(ApplicationV2) {
    constructor(message, options = {}) {
        super(options);

        this.message = message;
        this.damage = Object.keys(message.system.damage).reduce((acc, typeKey) => {
            const type = message.system.damage[typeKey];
            acc[typeKey] = Object.keys(type.parts).reduce((acc, partKey) => {
                const part = type.parts[partKey];
                acc[partKey] = Object.keys(part.dice).reduce((acc, diceKey) => {
                    const dice = part.dice[diceKey];
                    const activeResults = dice.results.filter(x => x.active);
                    acc[diceKey] = {
                        dice: dice.dice,
                        selectedResults: activeResults.length,
                        maxSelected: activeResults.length,
                        results: activeResults.map(x => ({ ...x, selected: true }))
                    };

                    return acc;
                }, {});

                return acc;
            }, {});

            return acc;
        }, {});
    }

    static DEFAULT_OPTIONS = {
        id: 'reroll-dialog',
        classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'reroll-dialog'],
        window: {
            icon: 'fa-solid fa-dice'
        },
        actions: {
            toggleResult: RerollDamageDialog.#toggleResult,
            selectRoll: RerollDamageDialog.#selectRoll,
            doReroll: RerollDamageDialog.#doReroll,
            save: RerollDamageDialog.#save
        }
    };

    /** @override */
    static PARTS = {
        main: {
            id: 'main',
            template: 'systems/daggerheart/templates/dialogs/rerollDialog/damage/main.hbs'
        },
        footer: {
            id: 'footer',
            template: 'systems/daggerheart/templates/dialogs/rerollDialog/footer.hbs'
        }
    };

    get title() {
        return game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.damageTitle');
    }

    _attachPartListeners(partId, htmlElement, options) {
        super._attachPartListeners(partId, htmlElement, options);

        htmlElement.querySelectorAll('.to-reroll-input').forEach(element => {
            element.addEventListener('change', this.toggleDice.bind(this));
        });
    }

    async _prepareContext(_options) {
        const context = await super._prepareContext(_options);
        context.damage = this.damage;
        context.disabledReroll = !this.getRerollDice().length;
        context.saveDisabled = !this.isSelectionDone();

        return context;
    }

    static async #save() {
        const update = {
            'system.damage': Object.keys(this.damage).reduce((acc, typeKey) => {
                const type = this.damage[typeKey];
                let typeTotal = 0;
                const messageType = this.message.system.damage[typeKey];
                const parts = Object.keys(type).map(partKey => {
                    const part = type[partKey];
                    const messagePart = messageType.parts[partKey];
                    let partTotal = messagePart.modifierTotal;
                    const dice = Object.keys(part).map(diceKey => {
                        const dice = part[diceKey];
                        const total = dice.results.reduce((acc, result) => {
                            if (result.active) acc += result.result;
                            return acc;
                        }, 0);
                        partTotal += total;
                        const messageDice = messagePart.dice[diceKey];
                        return {
                            ...messageDice,
                            total: total,
                            results: dice.results.map(x => ({
                                ...x,
                                hasRerolls: dice.results.length > 1
                            }))
                        };
                    });

                    typeTotal += partTotal;
                    return {
                        ...messagePart,
                        total: partTotal,
                        dice: dice
                    };
                });

                acc[typeKey] = {
                    ...messageType,
                    total: typeTotal,
                    parts: parts
                };

                return acc;
            }, {})
        };
        await this.message.update(update);

        Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll });
        await game.socket.emit(`system.${CONFIG.DH.id}`, {
            action: socketEvent.Refresh,
            data: {
                refreshType: RefreshType.TagTeamRoll
            }
        });

        await this.close();
    }

    getRerollDice() {
        const rerollDice = [];
        Object.keys(this.damage).forEach(typeKey => {
            const type = this.damage[typeKey];
            Object.keys(type).forEach(partKey => {
                const part = type[partKey];
                Object.keys(part).forEach(diceKey => {
                    const dice = part[diceKey];
                    Object.keys(dice.results).forEach(resultKey => {
                        const result = dice.results[resultKey];
                        if (result.toReroll) {
                            rerollDice.push({
                                ...result,
                                dice: dice.dice,
                                type: typeKey,
                                part: partKey,
                                dice: diceKey,
                                result: resultKey
                            });
                        }
                    });
                });
            });
        });

        return rerollDice;
    }

    isSelectionDone() {
        const diceFinishedData = [];
        Object.keys(this.damage).forEach(typeKey => {
            const type = this.damage[typeKey];
            Object.keys(type).forEach(partKey => {
                const part = type[partKey];
                Object.keys(part).forEach(diceKey => {
                    const dice = part[diceKey];
                    const selected = dice.results.reduce((acc, result) => acc + (result.active ? 1 : 0), 0);
                    diceFinishedData.push(selected === dice.maxSelected);
                });
            });
        });

        return diceFinishedData.every(x => x);
    }

    toggleDice(event) {
        const target = event.target;
        const { type, part, dice } = target.dataset;
        const toggleDice = this.damage[type][part][dice];

        const existingDiceRerolls = this.getRerollDice().filter(
            x => x.type === type && x.part === part && x.dice === dice
        );

        const allRerolled = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;

        toggleDice.toReroll = !allRerolled;
        toggleDice.results.forEach(result => {
            if (result.active) {
                result.toReroll = !allRerolled;
            }
        });

        this.render();
    }

    static #toggleResult(event) {
        event.stopPropagation();

        const target = event.target.closest('.to-reroll-result');
        const { type, part, dice, result } = target.dataset;
        const toggleDice = this.damage[type][part][dice];
        const toggleResult = toggleDice.results[result];
        toggleResult.toReroll = !toggleResult.toReroll;

        const existingDiceRerolls = this.getRerollDice().filter(
            x => x.type === type && x.part === part && x.dice === dice
        );

        const allToReroll = existingDiceRerolls.length === toggleDice.results.filter(x => x.active).length;
        toggleDice.toReroll = allToReroll;

        this.render();
    }

    static async #selectRoll(_, button) {
        const { type, part, dice, result } = button.dataset;

        const diceVal = this.damage[type][part][dice];
        const diceResult = diceVal.results[result];
        if (!diceResult.active && diceVal.results.filter(x => x.active).length === diceVal.maxSelected) {
            return ui.notifications.warn(
                game.i18n.localize('DAGGERHEART.APPLICATIONS.RerollDialog.deselectDiceNotification')
            );
        }

        if (diceResult.active) {
            diceVal.toReroll = false;
            diceResult.toReroll = false;
        }

        diceVal.selectedResults += diceResult.active ? -1 : 1;
        diceResult.active = !diceResult.active;

        this.render();
    }

    static async #doReroll() {
        const toReroll = this.getRerollDice().map(x => {
            const { type, part, dice, result } = x;
            const diceData = this.damage[type][part][dice].results[result];
            return {
                ...diceData,
                dice: this.damage[type][part][dice].dice,
                typeKey: type,
                partKey: part,
                diceKey: dice,
                resultsIndex: result
            };
        });

        const roll = await new Roll(toReroll.map(x => `1${x.dice}`).join(' + ')).evaluate();

        if (game.modules.get('dice-so-nice')?.active) {
            const diceSoNiceRoll = {
                _evaluated: true,
                dice: roll.dice,
                options: { appearance: {} }
            };

            await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);
        }

        toReroll.forEach((data, index) => {
            const { typeKey, partKey, diceKey, resultsIndex } = data;
            const rerolledDice = roll.dice[index];

            const dice = this.damage[typeKey][partKey][diceKey];
            dice.toReroll = false;
            dice.results[resultsIndex].active = false;
            dice.results[resultsIndex].discarded = true;
            dice.results[resultsIndex].toReroll = false;
            dice.results.splice(dice.results.length, 0, {
                ...rerolledDice.results[0],
                toReroll: false,
                selected: true
            });
        });

        this.render();
    }
}
