import { openActionMenu, promptTextInput } from "../../../api/ui.js";
import { applySessionWindowPlacement, rememberSessionWindowPlacement } from "/modules/augur-nexus/scripts/api/window-placement.js";
import { ConnectionsBoard } from "../../connections/components/ConnectionsBoard.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { DossierTabPreference } from "../../nexus/services/DossierTabPreference.js";
import { SiteCoverPicker } from "../../site/applications/SiteCoverPicker.js";
import { SiteCoverCatalog } from "../../site/services/SiteCoverCatalog.js";
import { ShopModel } from "../models/ShopModel.js";
import { ShopProfileViewModel } from "../models/ShopProfileViewModel.js";
import { ShopProductProviderRegistry } from "../services/ShopProductProviderRegistry.js";
import { ShopQuoteService } from "../services/ShopQuoteService.js";
import { ShopStore } from "../services/ShopStore.js";
import { ShopDerivedNameService } from "../services/ShopDerivedNameService.js";
import { ShopSystemAdapterRegistry } from "../services/ShopSystemAdapterRegistry.js";
import { ShopEditorRegistry } from "../services/ShopEditorRegistry.js";
import { ShopIconPicker } from "./ShopIconPicker.js";
import { ShopPresetRegistry } from "../services/ShopPresetRegistry.js";
import { ShopIconCatalogRegistry } from "../services/ShopIconCatalogRegistry.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const PLACEMENT_KEY = "augur-nexus.shop-dossier";
const JOURNAL_HOOKS = ["createJournalEntryPage", "updateJournalEntryPage", "deleteJournalEntryPage"];

export class ShopDossier extends HandlebarsApplicationMixin(ApplicationV2) {
    #shopId = "";
    #activeTab = "profile";
    #connectionsBoard = null;
    #shopsChangedHook = null;
    #connectionsChangedHook = null;
    #journalChangedHook = null;
    #lastModel = null;
    #positionInitialized = false;

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-shop-dossier",
        classes: ["augur-nexus", "npc-dossier", "shop-dossier"],
        tag: "div",
        window: { title: "Shop", resizable: true, minimizable: true },
        position: { width: 680, height: 600 },
        actions: {
            setShopDossierTab: ShopDossier._onSetTab,
            openShopActions: ShopDossier._onActions,
            openShopTrade: ShopDossier._onOpenShop,
            configureShop: ShopDossier._onConfigure,
            editShopJournal: ShopDossier._onJournal
        }
    };

    static PARTS = {
        content: {
            template: "modules/augur-nexus/templates/features/shops/shop-dossier.hbs",
            templates: [
                "modules/augur-nexus/templates/connections/connections-board.hbs",
                "modules/augur-nexus/templates/features/shops/shop-profile-panel.hbs"
            ]
        }
    };

    static show({ shopId = "" } = {}) {
        if (!shopId) return null;
        const app = new this({ shopId });
        app.render(true, { focus: true });
        return app;
    }

    constructor({ shopId = "" } = {}, options = {}) {
        super(options);
        this.#shopId = shopId;
        this.#activeTab = DossierTabPreference.getDefaultTab(ShopStore.getShop(shopId));
        this.#shopsChangedHook = event => { if (!event?.shop || event.shop.id === this.#shopId) this.refresh(); };
        this.#connectionsChangedHook = () => this.refresh();
        this.#journalChangedHook = page => {
            const document = ShopStore.resolveDocument(this.#shopId);
            if (page?.parent?.id === document?.id) this.refresh();
        };
        Hooks.on("augurNexusShopsChanged", this.#shopsChangedHook);
        Hooks.on("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        for (const hookName of JOURNAL_HOOKS) Hooks.on(hookName, this.#journalChangedHook);
    }

    get title() { return this.#lastModel?.shop?.name || "Shop"; }

    async _prepareContext() {
        const shop = ShopStore.getShop(this.#shopId);
        const document = ShopStore.resolveDocument(this.#shopId);
        if (!shop || !document) return { missing: true, shop: { name: "Missing Shop" }, connections: { board: null } };

        const target = ConnectionTargetResolver.fromDocument(document, { category: "service" });
        if (!this.#connectionsBoard) this.#connectionsBoard = new ConnectionsBoard({ target, isGM: game.user.isGM });
        else this.#connectionsBoard.setTarget(target);

        const page = ShopStore.getJournalPage(shop.id);
        const stock = await this.#buildStockPreview(shop);
        const model = {
            missing: false,
            isGM: game.user.isGM,
            shop: {
                id: shop.id,
                name: shop.name,
                profile: ShopProfileViewModel.fromShop(shop, {
                    activeTab: this.#activeTab,
                    showTabs: true,
                    showActions: game.user.isGM,
                    showBody: this.#activeTab === "profile",
                    stock
                })
            },
            dossier: {
                isProfileTab: this.#activeTab === "profile",
                isJournalTab: this.#activeTab === "journal"
            },
            journal: {
                hasPage: !!page,
                hasContent: !!String(page?.text?.content || "").trim(),
                content: await this.#enrich(page)
            },
            connections: { board: this.#connectionsBoard.getContext() },
            backgroundStyle: this.#buildBackgroundStyle(shop)
        };
        this.#lastModel = model;
        return model;
    }

    _onRender(context, options) {
        super._onRender(context, options);
        if (this.window?.title) this.window.title.textContent = this.title;
        this.#connectionsBoard?.activateListeners(this.element, this);
        if (!this.#positionInitialized) {
            applySessionWindowPlacement(this, PLACEMENT_KEY, { fallback: "center" });
            this.#positionInitialized = true;
        }
    }

    async close(options) {
        rememberSessionWindowPlacement(this, PLACEMENT_KEY);
        if (this.#shopsChangedHook) Hooks.off("augurNexusShopsChanged", this.#shopsChangedHook);
        if (this.#connectionsChangedHook) Hooks.off("augurNexusConnectionsChanged", this.#connectionsChangedHook);
        if (this.#journalChangedHook) for (const hookName of JOURNAL_HOOKS) Hooks.off(hookName, this.#journalChangedHook);
        return super.close(options);
    }

    refresh() { if (this.rendered !== false) this.render({ parts: ["content"] }); }

    static async _onSetTab(_event, target) {
        this.#activeTab = target?.dataset?.tab === "journal" ? "journal" : "profile";
        this.render({ parts: ["content"] });
    }

    static async _onActions(_event, target) {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        const naming = ShopDerivedNameService.getState(shop);
        openActionMenu({
            anchor: target,
            className: "shop-dossier-actions-menu",
            items: [
                { id: "rename", label: "Rename", icon: "fas fa-pen", onSelect: () => this.#renameShop() },
                ...(naming?.canRestore && !naming.isDerived ? [{ id: "restore-generated-name", label: "Restore Generated Name", icon: "fas fa-rotate-left", onSelect: () => this.#restoreGeneratedName() }] : []),
                { id: "configure", label: "Configure Shop", icon: "fas fa-sliders", onSelect: () => ShopDossier._onConfigure.call(this) },
                { id: "change-icon", label: "Change Icon", icon: "fas fa-store", onSelect: () => this.#changeIcon() },
                { id: "open-journal", label: "Edit Journal", icon: "fas fa-book-open", onSelect: () => ShopDossier._onJournal.call(this) },
                {
                    id: "default-dossier-tab",
                    label: `Default Tab: ${this.#getTabLabel(DossierTabPreference.getDefaultTab(shop))}`,
                    icon: "fas fa-table-columns",
                    onSelect: () => this.#openDefaultDossierTabMenu(target)
                },
                { id: "change-cover", label: "Change Cover Image", icon: "fas fa-image", onSelect: () => this.#changeCoverImage() },
                ...(ShopModel.getCoverImage(shop) ? [{ id: "clear-cover", label: "Clear Cover Image", icon: "fas fa-rotate-left", onSelect: () => this.#setCoverImage("") }] : []),
                { id: "delete", label: "Delete Shop", status: "Permanent", icon: "fas fa-skull-crossbones", danger: true, className: "permanent-danger", onSelect: () => this.#deleteShop() }
            ]
        });
    }

    static async _onOpenShop() {
        const { openShopTrade } = await import("../../../api/shops.js");
        const trade = openShopTrade(this.#shopId);
        if (trade) await this.close();
    }

    static async _onConfigure() {
        if (game.user.isGM) ShopEditorRegistry.open({ shopId: this.#shopId });
    }

    static async _onJournal() { await this.#editJournal(); }

    async #renameShop() {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        const name = await promptTextInput({ title: "Rename Shop", label: "Name", value: shop.name, confirmLabel: "Rename" });
        if (!name) return;
        await ShopStore.updateShop(shop.id, { name });
        this.refresh();
    }

    async #changeIcon() {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        const preset = ShopPresetRegistry.get(shop.restockProfile?.preset?.id);
        const defaultIcon = await ShopIconCatalogRegistry.getIcon(preset?.defaultImageId);
        new ShopIconPicker({
            currentImageSrc: ShopModel.getImage(shop),
            defaultIcon,
            onSelect: selection => void this.#setIcon(selection)
        }).render(true);
    }

    async #setIcon(selection) {
        const shop = ShopStore.getShop(this.#shopId);
        const path = String(selection?.src || "");
        if (!game.user.isGM || !shop || !path) return;
        await ShopStore.updateShop(shop.id, { display: {
            imageSrc: path,
            imageSelection: {
                mode: ["preset", "catalog", "custom"].includes(selection?.mode) ? selection.mode : "custom",
                id: String(selection?.id || "")
            }
        } });
        this.refresh();
    }

    async #restoreGeneratedName() {
        const restored = await ShopDerivedNameService.restore(this.#shopId);
        if (!restored) return ui.notifications.warn("This Shop does not have a generated parent-based name to restore.");
        ui.notifications.info(`${restored.name} now follows its parent Location name.`);
        this.refresh();
    }

    #openDefaultDossierTabMenu(anchor) {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        openActionMenu({
            anchor,
            className: "shop-dossier-actions-menu",
            items: ["profile", "journal"].map(tab => {
                const current = DossierTabPreference.getDefaultTab(shop);
                const label = this.#getTabLabel(tab);
                return {
                    id: `default-tab-${tab}`,
                    label: current === tab ? `${label} (Current)` : label,
                    icon: tab === "journal" ? "fas fa-book-open" : "fas fa-boxes-stacked",
                    onSelect: () => this.#setDefaultDossierTab(tab)
                };
            })
        });
    }

    async #setDefaultDossierTab(tab) {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        const defaultTab = DossierTabPreference.normalizeTab(tab);
        await ShopStore.updateShop(shop.id, { dossier: { defaultTab } });
        this.#activeTab = defaultTab;
        this.refresh();
    }

    #changeCoverImage() {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        SiteCoverPicker.show({ current: ShopModel.getCoverImage(shop), onSelect: path => this.#setCoverImage(path || "") });
    }

    async #setCoverImage(path) {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        await ShopStore.updateShop(shop.id, { display: { coverImageSrc: path || "" } });
        this.refresh();
    }

    async #editJournal() {
        if (!game.user.isGM) return;
        const page = await ShopStore.getOrCreateJournalPage(this.#shopId);
        if (!page) {
            ui.notifications.warn("No journal could be created for this shop.");
            return;
        }
        page.sheet?.render(true);
    }

    async #deleteShop() {
        const shop = ShopStore.getShop(this.#shopId);
        if (!game.user.isGM || !shop) return;
        const recoveryHint = String(shop.lifecycle?.recoveryHint || "").trim();
        const confirmed = await foundry.applications.api.DialogV2.confirm({
            window: { title: "Delete Shop" },
            content: `<p>Delete <strong>${foundry.utils.escapeHTML(shop.name)}</strong>?</p>${recoveryHint ? `<p class="notification warning">${foundry.utils.escapeHTML(recoveryHint)}</p>` : ""}`,
            modal: true,
            rejectClose: false,
            yes: { label: "Delete" },
            no: { label: "Cancel" }
        });
        if (!confirmed) return;
        await ShopStore.deleteShop(shop.id);
        await this.close();
    }

    async #enrich(page) {
        const content = page?.text?.content || "";
        if (!String(content).trim()) return "";
        try {
            return foundry.applications.ux.TextEditor.implementation.enrichHTML(content, { relativeTo: page, secrets: page.isOwner });
        } catch (_error) { return content; }
    }

    #buildBackgroundStyle(shop) {
        const cover = ShopModel.getCoverImage(shop) || SiteCoverCatalog.getAbstractCover(shop?.id || shop?.name || "shop");
        return cover ? `background-image: url('${cover}');` : "";
    }

    async #buildStockPreview(shop) {
        const adapter = ShopSystemAdapterRegistry.getActive();
        const finiteStock = shop?.configuration?.finiteStock !== false;
        const cards = await Promise.all((shop?.inventory || []).map(async row => {
            try {
                if (finiteStock && row.quantity <= 0) return null;
                const product = await ShopProductProviderRegistry.resolve(row.providerId, row.productId, { shop, row });
                if (!product) return null;
                const quote = await ShopQuoteService.quoteCustomerPurchase({ shop, row, product });
                const priceLabel = adapter?.formatPrice(quote.amount) || String(quote.amount);
                return {
                    name: String(product.name || "Merchandise"),
                    imageSrc: String(product.imageSrc || "icons/svg/item-bag.svg"),
                    priceLabel,
                    quantity: finiteStock ? row.quantity : "\u221e",
                    ariaLabel: `${product.name || "Merchandise"}, ${priceLabel}, quantity ${finiteStock ? row.quantity : "unlimited"}`
                };
            } catch (error) {
                console.warn(`[Augur Nexus] Failed to resolve Shop stock preview item ${row.productId || "unknown"}.`, error);
                return null;
            }
        }));
        return cards.filter(Boolean);
    }

    #getTabLabel(tab) {
        return DossierTabPreference.normalizeTab(tab) === "journal" ? "Journal" : "Stock";
    }
}
