import { ConnectionOpinionModel } from "../models/ConnectionOpinionModel.js";
import { ConnectionOpinionService } from "../services/ConnectionOpinionService.js";
import { ConnectionStore } from "../services/ConnectionStore.js";

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

export class ConnectionOpinionEditor extends HandlebarsApplicationMixin(ApplicationV2) {
    #edgeId = "";
    #observerNodeId = "";
    #onChange = null;
    #connectionsChangedHook = null;
    #entitiesChangedHook = null;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-connection-opinion-editor",
        classes: ["augur-nexus", "augur-theme-fantasy", "connection-opinion-editor"],
        tag: "div",
        window: {
            title: "Opinions",
            resizable: true,
            minimizable: false
        },
        position: {
            width: 520,
            height: 640
        }
    };

    static PARTS = {
        content: {
            template: "modules/augur-nexus/templates/connections/connection-opinion-editor.hbs"
        }
    };

    static show({ edgeId, observerNodeId, onChange = null } = {}) {
        const app = new this({ edgeId, observerNodeId, onChange });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ edgeId, observerNodeId, onChange = null } = {}, options = {}) {
        super(options);
        this.#edgeId = String(edgeId || "");
        this.#observerNodeId = String(observerNodeId || "");
        this.#onChange = onChange;
        this.#connectionsChangedHook = () => this.#refreshFromLiveData();
        this.#entitiesChangedHook = () => this.#refreshFromLiveData();
        Hooks.on("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        Hooks.on("augurNexusCampaignEntitiesChanged", this.#entitiesChangedHook);
    }

    async _prepareContext() {
        const edge = ConnectionStore.getConnectionEdge(this.#edgeId);
        const opinion = ConnectionOpinionService.resolvePerspective(edge, this.#observerNodeId);
        if (!edge || !opinion) return { missing: true };
        const reverseObserverNodeId = opinion.subjectNodeId;

        return {
            missing: false,
            isGM: game.user.isGM,
            opinion: {
                ...opinion,
                tierStyle: `--opinion-tier-color: ${opinion.tier.color}`,
                adjustments: opinion.adjustments.map(adjustment => ({
                    ...adjustment,
                    valueLabel: this.#formatValue(adjustment.value)
                })),
                automaticInfluences: opinion.automaticInfluences.map(influence => ({
                    ...influence,
                    valueLabel: this.#formatValue(influence.value),
                    details: influence.details.map(detail => ({
                        ...detail,
                        valueLabel: this.#formatValue(detail.value)
                    }))
                }))
            },
            reverseObserverNodeId
        };
    }

    _attachPartListeners(partId, htmlElement, options) {
        if (super._attachPartListeners) super._attachPartListeners(partId, htmlElement, options);
        this.#activateListeners(htmlElement instanceof HTMLElement ? htmlElement : htmlElement?.[0]);
    }

    _onRender(context, options) {
        super._onRender(context, options);
        this.#activateListeners(this.element);
    }

    async close(options) {
        if (this.#connectionsChangedHook) Hooks.off("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        if (this.#entitiesChangedHook) Hooks.off("augurNexusCampaignEntitiesChanged", this.#entitiesChangedHook);
        return super.close(options);
    }

    #activateListeners(element) {
        if (!element) return;
        const bind = (selector, eventName, handler) => {
            for (const target of element.querySelectorAll(selector)) {
                const key = `opinionBound${eventName}`;
                if (target.dataset[key]) continue;
                target.dataset[key] = "true";
                target.addEventListener(eventName, handler);
            }
        };

        bind("[data-opinion-reverse]", "click", event => {
            event.preventDefault();
            this.#observerNodeId = event.currentTarget.dataset.opinionReverse || "";
            this.render({ parts: ["content"] });
        });
        bind("[data-opinion-base]", "change", async event => {
            await ConnectionOpinionService.setBase(this.#edgeId, this.#observerNodeId, event.currentTarget.value);
            this.#notifyChanged();
        });
        bind("[data-opinion-add-adjustment]", "click", async event => {
            event.preventDefault();
            const result = await this.#promptAdjustment();
            if (!result) return;
            try {
                await ConnectionOpinionService.addAdjustment(
                    this.#edgeId,
                    this.#observerNodeId,
                    result.adjustment,
                    { bidirectional: result.bidirectional }
                );
                this.#notifyChanged();
            } catch (error) {
                ui.notifications.error(error?.message || "The opinion adjustment could not be added.");
            }
        });
        bind("[data-opinion-remove-adjustment]", "click", async event => {
            event.preventDefault();
            const adjustmentId = event.currentTarget.dataset.opinionRemoveAdjustment || "";
            if (!adjustmentId) return;
            await ConnectionOpinionService.removeAdjustment(this.#edgeId, this.#observerNodeId, adjustmentId);
            this.#notifyChanged();
        });
        bind("[data-opinion-stop-tracking]", "click", async event => {
            event.preventDefault();
            const confirmed = await foundry.applications.api.DialogV2.confirm({
                window: { title: "Stop Tracking Opinions?" },
                content: "<p>This removes both directional opinion bases and adjustments. The connection itself remains unchanged.</p>",
                yes: { label: "Stop Tracking", icon: "fas fa-eye-slash" },
                no: { label: "Cancel" },
                rejectClose: false
            });
            if (!confirmed) return;
            await ConnectionOpinionService.disable(this.#edgeId);
            if (typeof this.#onChange === "function") this.#onChange();
            await this.close();
        });
    }

    async #promptAdjustment() {
        const labelId = `augur-opinion-label-${foundry.utils.randomID()}`;
        const valueId = `augur-opinion-value-${foundry.utils.randomID()}`;
        const bidirectionalId = `augur-opinion-bidirectional-${foundry.utils.randomID()}`;
        return foundry.applications.api.DialogV2.wait({
            classes: ["dialog", "augur-nexus", "augur-theme-fantasy", "connection-opinion-adjustment-dialog"],
            window: { title: "Add Opinion Adjustment" },
            position: { width: 440 },
            modal: true,
            rejectClose: false,
            content: `
                <div class="nexus-text-prompt-form connection-opinion-adjustment-prompt">
                    <label class="connection-opinion-adjustment-field" for="${labelId}">
                        <span>Reason</span>
                        <input id="${labelId}" type="text" placeholder="Protected our settlement" autocomplete="off">
                    </label>
                    <label class="connection-opinion-adjustment-field" for="${valueId}">
                        <span>Value</span>
                        <input id="${valueId}" type="number" min="-100" max="100" step="1" value="10">
                    </label>
                    <label class="connection-opinion-adjustment-direction" for="${bidirectionalId}">
                        <input id="${bidirectionalId}" type="checkbox">
                        <i class="fas fa-arrow-right-arrow-left"></i>
                        <span><strong>Bidirectional Adjustment</strong><small>Apply the same adjustment to both parties' opinions of one another.</small></span>
                    </label>
                </div>
            `,
            buttons: [
                {
                    action: "confirm",
                    label: "Add Adjustment",
                    icon: "fas fa-plus",
                    default: true,
                    callback: () => {
                        const label = document.getElementById(labelId)?.value?.trim() || "";
                        const value = ConnectionOpinionModel.normalizeValue(document.getElementById(valueId)?.value);
                        const bidirectional = document.getElementById(bidirectionalId)?.checked === true;
                        return label && value
                            ? { adjustment: { label, value, source: { kind: "manual", id: null } }, bidirectional }
                            : null;
                    }
                },
                { action: "cancel", label: "Cancel", icon: "fas fa-xmark", callback: () => null }
            ]
        });
    }

    #notifyChanged() {
        if (typeof this.#onChange === "function") this.#onChange();
        this.render({ parts: ["content"] });
    }

    #refreshFromLiveData() {
        const edge = ConnectionStore.getConnectionEdge(this.#edgeId);
        if (!edge || !ConnectionOpinionService.isTracked(edge)) {
            this.close();
            return;
        }
        this.render({ parts: ["content"] });
    }

    #formatValue(value) {
        const number = Number(value || 0);
        return number > 0 ? `+${number}` : String(number);
    }
}
