import { AdversaryBPPerEncounter } from '../../config/encounterConfig.mjs';
import { expireActiveEffects } from '../../helpers/utils.mjs';
import { clearPreviousSpotlight } from '../../macros/spotlightCombatant.mjs';

export default class DhCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
    static DEFAULT_OPTIONS = {
        actions: {
            requestSpotlight: this.requestSpotlight,
            toggleSpotlight: this.toggleSpotlight,
            setActionTokens: this.setActionTokens
        }
    };

    static PARTS = {
        header: {
            template: 'systems/daggerheart/templates/ui/combatTracker/combatTrackerHeader.hbs'
        },
        tracker: {
            template: 'systems/daggerheart/templates/ui/combatTracker/combatTracker.hbs'
        },
        footer: {
            template: 'systems/daggerheart/templates/ui/combatTracker/combatTrackerFooter.hbs'
        }
    };

    /** @inheritDoc */
    async _preparePartContext(_partId, context, _options) {
        return context;
    }

    async _prepareContext(options) {
        const context = await super._prepareContext(options);

        await this._prepareTrackerContext(context, options);
        await this._prepareCombatContext(context, options);

        return context;
    }

    async _prepareCombatContext(context, options) {
        await super._prepareCombatContext(context, options);

        const modifierBP =
            this.combats
                .find(x => x.active)
                ?.system?.extendedBattleToggles?.reduce((acc, toggle) => (acc ?? 0) + toggle.category, null) ?? null;
        const maxBP = CONFIG.DH.ENCOUNTER.BaseBPPerEncounter(context.allCharacters.length) + modifierBP;
        const currentBP = AdversaryBPPerEncounter(context.adversaries, context.allCharacters);

        Object.assign(context, {
            fear: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear),
            battlepoints: { max: maxBP, current: currentBP, hasModifierBP: modifierBP !== null }
        });
    }

    async _prepareTrackerContext(context, options) {
        await super._prepareTrackerContext(context, options);

        const npcs = context.turns?.filter(x => x.isNPC) ?? [];
        const adversaries = npcs.filter(x => x.disposition !== CONST.TOKEN_DISPOSITIONS.FRIENDLY);
        const friendlies = npcs.filter(x => x.disposition === CONST.TOKEN_DISPOSITIONS.FRIENDLY);
        const characters = context.turns?.filter(x => !x.isNPC) ?? [];
        const spotlightQueueEnabled = game.settings.get(
            CONFIG.DH.id,
            CONFIG.DH.SETTINGS.gameSettings.SpotlightRequestQueue
        );

        const spotlightRequests = characters
            ?.filter(x => !x.isNPC && spotlightQueueEnabled)
            .filter(x => x.system.spotlight.requestOrderIndex > 0)
            .sort((a, b) => {
                const valueA = a.system.spotlight.requestOrderIndex;
                const valueB = b.system.spotlight.requestOrderIndex;
                return valueA - valueB;
            });

        Object.assign(context, {
            actionTokens: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).actionTokens,
            adversaries,
            friendlies,
            allCharacters: characters,
            characters: characters.filter(x => !spotlightQueueEnabled || x.system.spotlight.requestOrderIndex == 0),
            spotlightRequests
        });
    }

    _getCombatContextOptions() {
        return [
            {
                label: 'COMBAT.ClearMovementHistories',
                icon: '<i class="fa-solid fa-shoe-prints"></i>',
                visible: () => game.user.isGM && this.viewed?.combatants.size > 0,
                callback: () => this.viewed.clearMovementHistories()
            },
            {
                label: 'COMBAT.Delete',
                icon: '<i class="fa-solid fa-trash"></i>',
                visible: () => game.user.isGM && !!this.viewed,
                callback: () => this.viewed.endCombat()
            }
        ];
    }

    getDefeatedId(combatant) {
        if (!combatant.actor) return CONFIG.specialStatusEffects.DEFEATED;

        const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated;
        return settings[`${combatant.actor.type}Default`];
    }

    /** @inheritdoc */
    async _onToggleDefeatedStatus(combatant) {
        const isDefeated = !combatant.isDefeated;
        await combatant.update({ defeated: isDefeated });
        await combatant.actor?.toggleStatusEffect(this.getDefeatedId(combatant), { overlay: true, active: isDefeated });
    }

    /** @inheritdoc */
    async _prepareTurnContext(combat, combatant, index) {
        const { id, name, isOwner, isDefeated, hidden, initiative, permission } = combatant;
        const resource = permission >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER ? combatant.resource : null;
        const hasDecimals = Number.isFinite(initiative) && !Number.isInteger(initiative);
        const turn = {
            hasDecimals,
            hidden,
            id,
            isDefeated,
            initiative,
            isOwner,
            name,
            resource,
            active: index === combat.turn,
            canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'),
            type: combatant.actor?.system?.type,
            img: await this._getCombatantThumbnail(combatant),
            disposition: combatant.token.disposition
        };

        turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin(
            ' '
        );

        const defeatedId = this.getDefeatedId(combatant);
        const effects = [];
        for (const effect of combatant.actor?.temporaryEffects ?? []) {
            if (effect.statuses.has(defeatedId)) turn.isDefeated = true;
            else if (effect.img) effects.push({ img: effect.img, name: effect.name });
        }
        turn.effects = {
            icons: effects,
            tooltip: this._formatEffectsTooltip(effects)
        };

        return { ...turn, isNPC: combatant.isNPC, system: combatant.system.toObject() };
    }

    async setCombatantSpotlight(combatantId) {
        const combatant = this.viewed.combatants.get(combatantId);
        const update = {
            system: {
                'spotlight.requesting': false,
                'spotlight.requestOrderIndex': 0
            }
        };

        const toggleTurn = this.viewed.combatants.contents
            .sort(this.viewed._sortCombatants)
            .map(x => x.id)
            .indexOf(combatantId);

        if (this.viewed.turn !== toggleTurn) {
            const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
            if (combatant.actor?.type === 'character') {
                await updateCountdowns(
                    CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id,
                    CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id
                );
            } else {
                await updateCountdowns(CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id);
            }

            const autoPoints = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).actionPoints;
            if (autoPoints) {
                update.system.actionTokens = Math.max(combatant.system.actionTokens - 1, 0);
            }

            if (combatant.actor) expireActiveEffects(combatant.actor, [CONFIG.DH.GENERAL.activeEffectDurations.act.id]);
        }

        await this.viewed.update({
            turn: this.viewed.turn === toggleTurn ? null : toggleTurn,
            round: this.viewed.round + 1
        });
        await combatant.update(update);
        if (combatant.token) clearPreviousSpotlight();
    }

    async clearTurn() {
        await this.viewed.update({
            turn: null,
            round: this.viewed.round + 1
        });
    }

    static async requestSpotlight(_, target) {
        const characters = this.viewed.turns?.filter(x => !x.isNPC) ?? [];
        const orderValues = characters.map(character => character.system.spotlight.requestOrderIndex);
        const maxRequestIndex = Math.max(...orderValues);
        const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
        const combatant = this.viewed.combatants.get(combatantId);
        await combatant.update({
            'system.spotlight': {
                requesting: !combatant.system.spotlight.requesting,
                requestOrderIndex: !combatant.system.spotlight.requesting ? maxRequestIndex + 1 : 0
            }
        });

        this.render();
    }

    static async toggleSpotlight(_, target) {
        const { combatantId } = target.closest('[data-combatant-id]')?.dataset ?? {};
        await this.setCombatantSpotlight(combatantId);
    }

    static async setActionTokens(_, target) {
        const { combatantId, tokenIndex } = target.closest('[data-combatant-id]')?.dataset ?? {};

        const combatant = this.viewed.combatants.get(combatantId);
        const changeIndex = Number(tokenIndex);
        const newIndex = combatant.system.actionTokens > changeIndex ? changeIndex : changeIndex + 1;

        await combatant.update({ 'system.actionTokens': newIndex });
        this.render();
    }
}
