import { abilities } from '../../config/actorConfig.mjs';

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

export default class D20RollDialog extends HandlebarsApplicationMixin(ApplicationV2) {
    constructor(roll, config = {}, options = {}) {
        super(options);

        this.roll = roll;
        this.config = config;
        this.config.experiences = [];
        this.reactionOverride = config.actionType === 'reaction';
        this.selectedEffects = this.config.bonusEffects;

        if (config.source?.action) {
            this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent;
            this.action =
                config.data.attack?._id == config.source.action
                    ? config.data.attack
                    : this.item.system.actionsList?.find(a => a.id === config.source.action);
        }
    }

    static DEFAULT_OPTIONS = {
        tag: 'form',
        // id: 'roll-selection',
        classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'roll-selection'],
        position: {
            width: 'auto'
        },
        window: {
            icon: 'fa-solid fa-dice'
        },
        actions: {
            updateIsAdvantage: this.updateIsAdvantage,
            selectExperience: this.selectExperience,
            toggleReaction: this.toggleReaction,
            toggleSelectedEffect: this.toggleSelectedEffect,
            submitRoll: this.submitRoll
        },
        form: {
            handler: this.updateRollConfiguration,
            submitOnChange: true,
            submitOnClose: false
        }
    };

    get title() {
        return `${this.config.title}${this.actor ? `: ${this.actor.name}` : ''}`;
    }

    get actor() {
        return this.config?.data?.parent;
    }

    /** @override */
    static PARTS = {
        header: {
            id: 'header',
            template: 'systems/daggerheart/templates/dialogs/dice-roll/header.hbs'
        },
        rollSelection: {
            id: 'rollSelection',
            template: 'systems/daggerheart/templates/dialogs/dice-roll/rollSelection.hbs'
        }
    };

    async _prepareContext(_options) {
        const context = await super._prepareContext(_options);
        context.rollConfig = this.config;
        context.hasRoll = !!this.config.roll;
        context.canRoll = true;
        context.selectedMessageMode = this.config.selectedMessageMode ?? game.settings.get('core', 'messageMode');
        context.rollModes = Object.entries(CONFIG.ChatMessage.modes).map(([action, { label, icon }]) => ({
            action,
            label,
            icon
        }));

        context.hasSelectedEffects = Boolean(this.selectedEffects && Object.keys(this.selectedEffects).length);
        context.selectedEffects = this.selectedEffects;

        this.config.costs ??= [];
        if (this.config.costs?.length) {
            const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call(
                this.action ?? { actor: this.actor },
                this.config.costs
            );
            context.costs = updatedCosts.map(x => ({
                ...x,
                label: x.itemId
                    ? this.action.parent.parent.name
                    : game.i18n.localize(CONFIG.DH.GENERAL.abilityCosts[x.key].label)
            }));
            context.canRoll = game.system.api.fields.ActionFields.CostField.hasCost.call(
                this.action ?? { actor: this.actor },
                updatedCosts
            );
            this.config.data.scale = this.config.costs[0].total;
        }
        if (this.config.uses?.max) {
            context.uses = game.system.api.fields.ActionFields.UsesField.calcUses.call(this.action, this.config.uses);
            context.canRoll =
                context.canRoll &&
                game.system.api.fields.ActionFields.UsesField.hasUses.call(this.action, context.uses);
        }
        if (this.roll) {
            context.roll = this.roll;
            context.rollType = this.roll?.constructor.name;
            context.rallyDie = this.roll.rallyChoices;

            const actorExperiences = this.config.data?.system?.experiences || {};
            const companionExperiences = this.config.roll.companionRoll
                ? (this.config.data?.companion?.system.experiences ?? {})
                : null;
            const experiences = companionExperiences ?? actorExperiences;
            context.experiences = Object.keys(experiences).map(id => ({
                id,
                ...experiences[id]
            }));

            context.selectedExperiences = this.config.experiences;
            context.advantage = this.config.roll?.advantage;
            context.disadvantage = this.config.roll?.disadvantage;
            context.diceOptions = CONFIG.DH.GENERAL.diceTypes;
            context.dieFaces = CONFIG.DH.GENERAL.dieFaces.reduce((acc, face) => {
                acc[face] = `d${face}`;
                return acc;
            }, {});
            context.isLite = this.config.roll?.lite;
            context.extraFormula = this.config.extraFormula;
            context.formula = this.roll.constructFormula(this.config);
            if (this.actor?.system?.traits) context.abilities = this.getTraitModifiers();

            context.showReaction = !this.config.skips?.reaction && context.rollType === 'DualityRoll';
            context.reactionOverride = this.reactionOverride;
        }

        return context;
    }

    getTraitModifiers() {
        return Object.values(abilities).map(a => ({
            id: a.id,
            label: `${game.i18n.localize(a.label)} (${this.actor.system.traits[a.id]?.value.signedString() ?? 0})`
        }));
    }

    static updateRollConfiguration(_event, _, formData) {
        const { ...rest } = foundry.utils.expandObject(formData.object);

        this.config.selectedMessageMode = rest.selectedMessageMode;

        if (this.config.costs) {
            this.config.costs = foundry.utils.mergeObject(this.config.costs, rest.costs);
        }
        if (this.config.uses) this.config.uses = foundry.utils.mergeObject(this.config.uses, rest.uses);
        if (rest.roll?.dice) {
            this.roll = foundry.utils.mergeObject(this.roll, rest.roll.dice);
        }
        if (rest.hasOwnProperty('trait')) {
            this.config.roll.trait = rest.trait;
            if (!this.config.source.item)
                this.config.title = game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
                    ability: game.i18n.localize(abilities[this.config.roll.trait]?.label)
                });
        }
        this.config.extraFormula = rest.extraFormula;
        this.render();
    }

    static updateIsAdvantage(_, button) {
        const advantage = Number(button.dataset.advantage);
        this.advantage = advantage === 1;
        this.disadvantage = advantage === -1;

        this.config.roll.advantage = this.config.roll.advantage === advantage ? 0 : advantage;
        if (this.config.roll.advantage === 0) return this.render();

        const defaultFaces =
            this.config.roll.advantage === 1
                ? this.config.data.rules.roll.defaultAdvantageDice
                : this.config.data.rules.roll.defaultDisadvantageDice;
        const faces = Number.parseInt(defaultFaces);
        this.roll.advantageFaces = Number.isNaN(faces) ? this.roll.advantageFaces : faces;

        this.render();
    }

    static selectExperience(_, button) {
        this.config.experiences =
            this.config.experiences.indexOf(button.dataset.key) > -1
                ? this.config.experiences.filter(x => x !== button.dataset.key)
                : [...this.config.experiences, button.dataset.key];
        this.config.costs =
            this.config.costs.indexOf(this.config.costs.find(c => c.extKey === button.dataset.key)) > -1
                ? this.config.costs.filter(x => x.extKey !== button.dataset.key)
                : [
                      ...this.config.costs,
                      {
                          extKey: button.dataset.key,
                          key: this.config?.data?.parent?.isNPC ? 'fear' : 'hope',
                          value: 1,
                          name: this.config.data?.system.experiences?.[button.dataset.key]?.name
                      }
                  ];
        this.render();
    }

    static toggleReaction() {
        if (this.config.roll) {
            this.reactionOverride = !this.reactionOverride;
            this.config.actionType = this.reactionOverride
                ? 'reaction'
                : this.config.actionType === 'reaction'
                  ? 'action'
                  : this.config.actionType;
            this.render();
        }
    }

    static toggleSelectedEffect(_event, button) {
        this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected;
        this.render();
    }

    static async submitRoll() {
        await this.close({ submitted: true });
    }

    /** @override */
    _onClose(options = {}) {
        if (!options.submitted) this.config = false;
    }

    static async configure(roll, config = {}, options = {}) {
        return new Promise(resolve => {
            const app = new this(roll, config, options);
            app.addEventListener('close', () => resolve(app.config), { once: true });
            app.render({ force: true });
        });
    }
}
