import ActionSelectionDialog from '../applications/dialogs/actionSelectionDialog.mjs';

/**
 * Override and extend the basic Item implementation.
 * @extends {foundry.documents.Item}
 */
export default class DHItem extends foundry.documents.Item {
    /** @inheritDoc */
    prepareEmbeddedDocuments() {
        super.prepareEmbeddedDocuments();
        for (const action of this.system.actions ?? []) action.prepareData();
    }

    /** @inheritDoc */
    getEmbeddedDocument(embeddedName, id, options) {
        let doc;
        switch (embeddedName) {
            case 'Action':
                doc = this.system.actions?.get(id);
                if (!doc && this.system.attack?.id === id) doc = this.system.attack;
                break;
            default:
                return super.getEmbeddedDocument(embeddedName, id, options);
        }
        if (options?.strict && !doc) {
            throw new Error(`The key ${id} does not exist in the ${embeddedName} Collection`);
        }
        return doc;
    }

    static async createDocuments(sources, operation) {
        // Ensure that items being created are valid to the actor its being added to
        const actor = operation.parent;
        const filtered = actor ? sources.filter(s => actor.system.isItemValid(s)) : sources;
        if (actor && filtered.length === 0 && sources.length > 0) {
            const itemType = _loc(`TYPES.Item.${sources[0].type}`);
            const actorType = _loc(`TYPES.Actor.${actor.type}`);
            ui.notifications.error('DAGGERHEART.ACTORS.Base.CannotAddType', { format: { itemType, actorType } });
        }
        return super.createDocuments(filtered, operation);
    }

    /* -------------------------------------------- */

    /** @inheritDoc */
    static migrateData(source) {
        if (source.system?.attack && !source.system.attack.type) source.system.attack.type = 'attack';
        return super.migrateData(source);
    }

    /**
     * @inheritdoc
     * @param {object} options - Options which modify the getRollData method.
     * @returns
     */
    getRollData(options = {}) {
        let data = this.system.getRollData(options);
        if (data?.item) {
            data.item.flags = { ...this.flags };
            data.item.name = this.name;
        }
        return data;
    }

    /**
     * Determine if this item is classified as an inventory item based on its metadata.
     * @returns {boolean} Returns `true` if the item is an inventory item.
     */
    get isInventoryItem() {
        return this.system.metadata.isInventoryItem ?? false;
    }

    /** Returns true if the item can be used */
    get usable() {
        const actor = this.actor;
        const actionsList = this.system.actionsList;
        return this.isOwner && actor?.type === 'character' && (actionsList?.size || actionsList?.length);
    }

    /** @inheritdoc */
    static async createDialog(data = {}, createOptions = {}, options = {}) {
        const { folders, types, template, context = {}, ...dialogOptions } = options;

        if (types?.length === 0) {
            throw new Error('The array of sub-types to restrict to must not be empty.');
        }

        const documentTypes = this.TYPES.filter(type => type !== 'base' && (!types || types.includes(type))).map(
            type => {
                const labelKey = CONFIG.Item?.typeLabels?.[type];
                const label = labelKey && game.i18n.has(labelKey) ? game.i18n.localize(labelKey) : type;

                const isInventoryItem = CONFIG.Item.dataModels[type]?.metadata?.isInventoryItem;
                const group =
                    isInventoryItem === true
                        ? 'Inventory Items' //TODO localize
                        : isInventoryItem === false
                          ? 'Character Items' //TODO localize
                          : 'Other'; //TODO localize

                return { value: type, label, group };
            }
        );

        if (!documentTypes.length) {
            throw new Error('No document types were permitted to be created.'); //TODO localize
        }

        const sortedTypes = documentTypes.sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang));

        return await super.createDialog(data, createOptions, {
            folders,
            types,
            template,
            context: { types: sortedTypes, ...context },
            ...dialogOptions
        });
    }

    /* -------------------------------------------- */

    /**
     * Generate an array of localized tag.
     * @returns {string[]} An array of localized tag strings.
     */
    _getTags() {
        const tags = [];
        if (this.system._getTags) tags.push(...this.system._getTags());
        return tags;
    }

    /**
     * Generate a localized label array for this item.
     * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects.
     */
    _getLabels() {
        const labels = [];
        if (this.system._getLabels) labels.push(...this.system._getLabels());
        return labels;
    }

    /* -------------------------------------------- */

    /**@inheritdoc */
    static getDefaultArtwork(itemData) {
        const { type } = itemData;
        const Model = CONFIG.Item.dataModels[type];
        const img = Model.DEFAULT_ICON ?? this.DEFAULT_ICON;
        return { img };
    }

    /* -------------------------------------------- */

    async use(event) {
        /* DomainCard check. Can be expanded or made neater */
        if (this.system.isDomainTouchedSuppressed) {
            return ui.notifications.warn(
                game.i18n.format('DAGGERHEART.UI.Notifications.domainTouchRequirement', {
                    nr: this.domainTouched,
                    domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[this.domain].label)
                })
            );
        }

        const actions = new Set(this.system.actionsList);
        if (actions?.size) {
            let action = actions.first();
            if (actions.size > 1 && !event?.shiftKey) {
                // Actions Choice Dialog
                action = await ActionSelectionDialog.create(this, event);
            }
            if (action) return action.use(event);
        }
    }

    /**
     * Create a new ChatMessage to display this document’s data
     * @param {String} origin - uuid of a document. TODO: This needs to be reviewed.
     */
    async toChat(origin) {
        /**@type {foundry.documents.ChatMessage} */
        const cls = getDocumentClass('ChatMessage');
        const item = await foundry.utils.fromUuid(origin);

        const systemData = {
            origin: origin,
            img: this.img,
            item: {
                name: this.name,
                img: this.img,
                tags: this._getTags()
            },
            actions: item.system.actionsList,
            description: await foundry.applications.ux.TextEditor.implementation.enrichHTML(this.system.description, {
                relativeTo: this.parent,
                rollData: this.parent?.getRollData() ?? {}
            })
        };

        const msg = {
            type: 'abilityUse',
            user: game.user.id,
            actor: item.parent,
            speaker: cls.getSpeaker(),
            system: systemData,
            content: await foundry.applications.handlebars.renderTemplate(
                'systems/daggerheart/templates/ui/chat/ability-use.hbs',
                systemData
            ),
            flags: {
                daggerheart: {
                    cssClass: 'dh-chat-message dh-style'
                }
            }
        };

        cls.create(msg);
    }

    deleteTriggers() {
        const actions = Array.from(this.system.actions ?? []);
        if (!actions.length) return;

        const triggerKeys = actions.flatMap(action => action.triggers.map(x => x.trigger));

        game.system.registeredTriggers.unregisterTriggers(triggerKeys, this.uuid);

        if (this.actor && !(this.actor.parent instanceof game.system.api.documents.DhToken)) {
            for (const token of this.actor.getActiveTokens()) {
                game.system.registeredTriggers.unregisterTriggers(triggerKeys, `${token.document.uuid}.${this.uuid}`);
            }
        }
    }

    async _preDelete() {
        this.deleteTriggers();
    }

    /** @inheritDoc */
    static migrateData(source) {
        const documentClass = game.system.api.data.items[`DH${source.type?.capitalize()}`];
        if (documentClass?.migrateDocumentData) {
            documentClass.migrateDocumentData(source);
        }

        return super.migrateData(source);
    }
}
