import {
    getChildLocations,
    getLocation,
    getLocations,
    getLocationConnectionTarget,
    getLocationOverview,
    getResolvedLocationCover,
    openLocation,
    createLocationDossierPanel
} from "../../../api/locations.js";
import { openNpcCreator, getNpcConnectionTarget, openNpcDossier } from "../../../api/npcs.js";
import { getFactionConnectionTarget, openFactionDossier } from "../../../api/factions.js";
import { addConnection, createConnectionsBoardController, resolveConnectionDrop, setConnectionDragData } from "../../../api/connections.js";
import { getOwnership, setOwner } from "../../../api/ownership.js";
import { getLocationLeader, setLocationLeader } from "../../../api/leadership.js";
import { SettlementDossierProviderRegistry } from "../services/SettlementDossierProviderRegistry.js";
import { LocationEntityModel } from "../models/LocationEntityModel.js";
import { getShopsForParentLocation, openShopCreator, openShopTrade } from "../../../api/shops.js";
import { canSetTradingCenter, clearTradingCenter, openTradingCenter, setTradingCenter } from "../../../api/trade-connections.js";
import { TradeBasinEconomyService } from "../../trade/services/TradeBasinEconomyService.js";
import { TradeRegionSceneService } from "../../trade/services/TradeRegionSceneService.js";
import { TradeRouteService } from "../../trade/services/TradeRouteService.js";
import { RegionalTradeNetworkApplication } from "../../trade/applications/RegionalTradeNetworkApplication.js";
import { TradeGoodDetailApplication } from "../../trade/applications/TradeGoodDetailApplication.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
const COMPACT_WIDTH = 430;
let compactPreference = false;

export class SettlementDossier extends HandlebarsApplicationMixin(ApplicationV2) {
    #locationId = "";
    #entity = null;
    #overview = null;
    #dossierPanel = null;
    #connectionsBoard = null;
    #isCompact = false;
    #expandedPosition = null;
    #changedHook = null;
    #provider = null;
    #providerContext = null;
    #providerState = { sceneId: "", sceneIdResolved: false };
    #cards = [];
    #economyHtml = "";
    #refreshHookNames = [
        "augurNexusCampaignEntitiesChanged",
        "augurNexusConnectionsChanged",
        "augurNexusShopsChanged",
        "augurNexusTradeGoodsChanged"
    ];

    static DEFAULT_OPTIONS = {
        id: "augur-nexus-settlement-dossier",
        classes: ["augur-nexus", "settlement-view-window", "settlement-experience-window", "augur-theme-fantasy"],
        tag: "div",
        window: { title: "Settlement", resizable: true, minimizable: true },
        position: { width: 1420, height: 780 },
        actions: {
            toggleSettlementCompact: SettlementDossier._onToggleCompact,
            openSettlementOwner: SettlementDossier._onOpenOwner,
            openSettlementLeader: SettlementDossier._onOpenLeader,
            openSettlementSite: SettlementDossier._onOpenSite,
            openSettlementPerson: SettlementDossier._onOpenPerson,
            createSettlementPerson: SettlementDossier._onCreatePerson,
            createSettlementShop: SettlementDossier._onCreateEstablishment,
            recreateSettlementMarketplace: SettlementDossier._onRecreateMarketplace,
            openSettlementTrade: SettlementDossier._onOpenTrade,
            openSettlementTradeGood: SettlementDossier._onOpenTradeGood
        }
    };

    static PARTS = {
        content: {
            template: "modules/augur-nexus/templates/campaign-entities/settlement-dossier.hbs",
            templates: ["modules/augur-nexus/templates/campaign-entities/settlement-domain-panels.hbs"]
        }
    };

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

    constructor({ locationId = "" } = {}, options = {}) {
        super(options);
        this.#locationId = String(locationId || "");
        this.#isCompact = compactPreference;
        this.#changedHook = () => this.#refresh();
        for (const hookName of this.#refreshHookNames) Hooks.on(hookName, this.#changedHook);
    }

    get title() { return this.#entity?.display?.name || "Settlement"; }

    async _prepareContext() {
        this.#entity = getLocation(this.#locationId);
        if (!this.#entity) return { missing: true };
        this.#overview = getLocationOverview(this.#entity) || { owner: null, leader: null, trade: null, people: [] };
        this.#provider = SettlementDossierProviderRegistry.getFor(this.#entity);
        this.#providerContext = this.#provider?.prepare
            ? await this.#provider.prepare({ entity: this.#entity, overview: this.#overview, state: this.#providerState }) || {}
            : {};
        if (!this.#dossierPanel) {
            this.#dossierPanel = await createLocationDossierPanel({
                locationId: this.#locationId,
                showProfileTab: true,
                renderProfileExtension: () => this.#renderCompactExtension()
            });
        }
        if (!this.#connectionsBoard) {
            this.#connectionsBoard = createConnectionsBoardController({
                target: getLocationConnectionTarget(this.#locationId),
                isGM: game.user.isGM
            });
        }
        const genericCards = [
            ...getChildLocations(this.#locationId).map(location => ({
                kind: "location",
                id: location.id,
                name: location.display?.name || "Location",
                subtitle: location.profile?.type || location.profile?.kindLabel || "Location",
                imageSrc: location.display?.thumbnailSrc || location.display?.imageSrc || "",
                icon: "fas fa-location-dot"
            })),
            ...getShopsForParentLocation(this.#locationId).map(shop => ({
                kind: "shop",
                id: shop.id,
                name: shop.name || "Shop",
                subtitle: "Shop",
                imageSrc: shop.display?.imageSrc || "",
                icon: "fas fa-store"
            }))
        ];
        const providerCards = Array.isArray(this.#providerContext.cards) ? this.#providerContext.cards : [];
        const cards = [...new Map([...genericCards, ...providerCards].map(card => [`${card.kind}:${card.id}`, card])).values()];
        this.#cards = cards;
        const missingMarketplace = this.#providerContext.missingMarketplace === true;
        if (!this.#providerState.sceneIdResolved) {
            this.#providerState.sceneId = await TradeRegionSceneService.chooseSceneId(this.#entity.id, { sceneId: this.#providerState.sceneId || "" });
            this.#providerState.sceneIdResolved = true;
        }
        const economyHtml = await foundry.applications.handlebars.renderTemplate(
            "modules/augur-nexus/templates/trade/settlement-economy.hbs",
            await TradeBasinEconomyService.getLocationView(this.#entity, { sceneId: this.#providerState.sceneId, user: game.user })
        );
        this.#economyHtml = economyHtml;
        return {
            missing: false,
            isGM: game.user.isGM,
            isCompact: this.#isCompact,
            backgroundSrc: this.#providerContext.backgroundSrc || getResolvedLocationCover(this.#entity),
            overview: this.#overview,
            cards,
            economyHtml,
            hasEconomy: true,
            missingMarketplace,
            establishmentCount: cards.length + Number(missingMarketplace),
            hasEstablishmentCards: cards.length > 0,
            hasFewEstablishments: cards.length + Number(missingMarketplace) <= 3,
            canCreateEstablishment: game.user.isGM && this.#providerContext.canCreateEstablishment !== false,
            canManageTrade: game.user.isGM || !!this.#overview.trade,
            dossierHtml: await this.#dossierPanel.render()
        };
    }

    _onRender(context, options) {
        super._onRender(context, options);
        if (this.window?.title) this.window.title.textContent = this.title;
        this.#dossierPanel?.activateListeners(this.element, this);
        this.#bindAuthorityDrops();
        this.#bindAuthorityDrag();
        this.#bindPeopleDrop();
        this.#bindPeopleDrag();
        this.#bindTradeDrop();
    }

    async close(options) {
        this.#dossierPanel?.destroy();
        if (this.#changedHook) {
            for (const hookName of this.#refreshHookNames) Hooks.off(hookName, this.#changedHook);
        }
        return super.close(options);
    }

    matchesCampaignEntityJournal(data = {}) { return this.#dossierPanel?.matchesCampaignEntityJournal(data) === true; }
    refreshCampaignEntityDossier() { this.#refresh(); }

    static _onToggleCompact() { this.#toggleCompact(); }
    static _onOpenOwner() {
        const owner = this.#overview?.owner;
        if (owner?.entityType === "faction") return openFactionDossier(owner.entityId);
        if (owner?.entityType === "location") return openLocation(owner.entityId);
    }
    static _onOpenLeader() { if (this.#overview?.leader?.entityId) return openNpcDossier(this.#overview.leader.entityId); }
    static _onOpenSite(_event, target) {
        const id = String(target?.dataset?.cardId || "");
        if (!id) return;
        if (this.#provider?.openEstablishment) {
            return this.#provider.openEstablishment({ entity: this.#entity, state: this.#providerState, kind: target.dataset.cardKind || "", id });
        }
        if (target.dataset.cardKind === "shop") return openShopTrade(id);
        return openLocation(id);
    }
    static _onOpenPerson(_event, target) { if (target?.dataset?.personId) return openNpcDossier(target.dataset.personId); }
    static _onCreatePerson() { if (game.user?.isGM) return this.#createPerson(); }
    static _onCreateEstablishment() {
        if (!game.user?.isGM) return;
        if (this.#provider?.createEstablishment) return this.#provider.createEstablishment({ entity: this.#entity, state: this.#providerState, refresh: () => this.#refresh() });
        return openShopCreator({ locationId: this.#entity.id, onSaved: () => this.#refresh() });
    }
    static async _onRecreateMarketplace() {
        if (!game.user?.isGM || !this.#provider?.recreateMarketplace) return;
        try {
            return await this.#provider.recreateMarketplace({ entity: this.#entity, state: this.#providerState, refresh: () => this.#refresh() });
        } catch (error) {
            console.error("Augur: Nexus | Failed to recreate settlement marketplace", error);
            ui.notifications.error(error?.message || "The Marketplace could not be recreated.");
        }
    }
    static _onOpenTrade() {
        return RegionalTradeNetworkApplication.show({ locationId: this.#entity.id, sceneId: this.#providerState.sceneId, scope: "connected" });
    }
    static _onOpenTradeGood(_event, target) {
        const goodId = String(target?.dataset?.goodId || "");
        if (goodId) return TradeGoodDetailApplication.show({ goodId, locationId: this.#entity.id, sceneId: this.#providerState.sceneId });
    }

    #createPerson() {
        const locationTarget = getLocationConnectionTarget(this.#locationId);
        return openNpcCreator({ onCreated: async npc => {
            const npcTarget = getNpcConnectionTarget(npc);
            if (locationTarget && npcTarget) {
                await addConnection(locationTarget, npcTarget, {
                    sourceView: { category: "npc", role: "Resident" },
                    targetView: { category: "place", role: "Located At" }
                });
            }
            this.#refresh();
        } });
    }

    #bindAuthorityDrops() {
        if (!game.user?.isGM) return;
        for (const surface of this.element?.querySelectorAll?.("[data-settlement-authority-drop]") || []) {
            surface.addEventListener("dragover", event => { event.preventDefault(); surface.classList.add("is-drop-target"); });
            surface.addEventListener("dragleave", event => { if (!surface.contains(event.relatedTarget)) surface.classList.remove("is-drop-target"); });
            surface.addEventListener("drop", event => {
                event.preventDefault();
                surface.classList.remove("is-drop-target");
                void this.#handleAuthorityDrop(event, surface.dataset.settlementAuthorityDrop || "");
            });
        }
    }

    #bindAuthorityDrag() {
        if (!game.user?.isGM) return;
        for (const card of this.element?.querySelectorAll?.("[data-settlement-authority-drag][draggable='true']") || []) {
            card.addEventListener("dragstart", event => {
                const entityType = card.dataset.authorityEntityType || "";
                const target = entityType === "faction"
                    ? getFactionConnectionTarget(card.dataset.authorityEntityId)
                    : entityType === "npc"
                        ? getNpcConnectionTarget(card.dataset.authorityEntityId)
                        : null;
                if (!target || !setConnectionDragData(event.dataTransfer, target, { effectAllowed: "copy" })) event.preventDefault();
            });
        }
    }

    #bindPeopleDrop() {
        const surface = this.element?.querySelector?.("[data-settlement-people-drop]");
        if (!game.user?.isGM || !surface) return;
        surface.addEventListener("dragover", event => { event.preventDefault(); surface.classList.add("is-drop-target"); });
        surface.addEventListener("dragleave", event => { if (!surface.contains(event.relatedTarget)) surface.classList.remove("is-drop-target"); });
        surface.addEventListener("drop", async event => {
            event.preventDefault();
            surface.classList.remove("is-drop-target");
            try { await this.#connectionsBoard.connectFromDrop(event); }
            catch (error) { ui.notifications.error(error?.message || "That connection could not be created."); }
            this.#refresh();
        });
    }

    #bindPeopleDrag() {
        if (!game.user?.isGM) return;
        for (const card of this.element?.querySelectorAll?.("[data-settlement-person-card][draggable='true']") || []) {
            card.addEventListener("dragstart", event => {
                const target = getNpcConnectionTarget(card.dataset.personId || "");
                if (!target || !setConnectionDragData(event.dataTransfer, target, { effectAllowed: "copyMove" })) event.preventDefault();
            });
        }
    }

    #bindTradeDrop() {
        if (!game.user?.isGM) return;
        for (const surface of this.element?.querySelectorAll?.("[data-settlement-trade-drop]") || []) {
            surface.addEventListener("dragover", event => { event.preventDefault(); surface.classList.add("is-drop-target"); });
            surface.addEventListener("dragleave", event => { if (!surface.contains(event.relatedTarget)) surface.classList.remove("is-drop-target"); });
            surface.addEventListener("drop", async event => {
                event.preventDefault();
                surface.classList.remove("is-drop-target");
                const target = await resolveConnectionDrop(event);
                if (target?.kind !== "nexus-entity" || target.entityType !== "location") {
                    ui.notifications.warn("Drop a Nexus Location here.");
                    return;
                }
                try {
                    await TradeRouteService.changeTradingCenter({
                        locationId: this.#entity.id,
                        centerLocationId: target.entityId,
                        sceneId: this.#providerState.sceneId
                    });
                    this.#refresh();
                } catch (error) {
                    console.error("Augur: Nexus | Failed to change settlement trading center", error);
                    ui.notifications.error(error?.message || "The trade route could not be changed.");
                }
            });
        }
    }

    async #openGenericTradePosition() {
        const locationTarget = getLocationConnectionTarget(this.#entity);
        if (!locationTarget) return;
        if (!game.user?.isGM) {
            const opened = await openTradingCenter(locationTarget);
            if (!opened) ui.notifications.info("This Location is the end of its trade route.");
            return opened;
        }

        const currentId = String(this.#overview?.trade?.kind === "through" ? this.#overview.trade.entityId || "" : "");
        const candidates = getLocations()
            .filter(location => location.id !== this.#entity.id && location.profile?.kindId === "settlement")
            .map(location => ({ location, target: getLocationConnectionTarget(location) }))
            .filter(entry => entry.target && (entry.location.id === currentId || canSetTradingCenter(locationTarget, entry.target)))
            .sort((left, right) => LocationEntityModel.getName(left.location).localeCompare(LocationEntityModel.getName(right.location)));
        const options = candidates.map(({ location }) => {
            const id = foundry.utils.escapeHTML(location.id);
            const name = foundry.utils.escapeHTML(LocationEntityModel.getName(location));
            const subtitle = foundry.utils.escapeHTML(LocationEntityModel.getSubtitle(location));
            return `<option value="${id}" ${location.id === currentId ? "selected" : ""}>${name} — ${subtitle}</option>`;
        }).join("");
        const selectedId = await foundry.applications.api.DialogV2.wait({
            window: { title: "Trade Position", icon: "fa-solid fa-route" },
            position: { width: 440 },
            content: `<form class="standard-form"><div class="form-group"><label>Trades Through</label><div class="form-fields"><select name="tradingCenterId"><option value="">Independent / No Destination</option>${options}</select></div><p class="hint">Choose the settlement that receives goods from this Location.</p></div></form>`,
            rejectClose: false,
            modal: true,
            buttons: [
                { action: "save", label: "Save", icon: "fa-solid fa-check", default: true, callback: (_event, button) => String(button.form?.elements?.tradingCenterId?.value || "") },
                { action: "cancel", label: "Cancel", icon: "fa-solid fa-xmark", callback: () => null }
            ]
        });
        if (selectedId === null || selectedId === currentId) return;
        try {
            if (!selectedId) await clearTradingCenter(locationTarget);
            else {
                const centerTarget = getLocationConnectionTarget(selectedId);
                if (!centerTarget) throw new Error("That trade destination could not be found.");
                await setTradingCenter(locationTarget, centerTarget);
            }
            this.#refresh();
        } catch (error) {
            console.error("Augur: Nexus | Failed to update generic trade position", error);
            ui.notifications.error(error?.message || "The trade position could not be updated.");
        }
    }

    async #handleAuthorityDrop(event, action) {
        const relatedTarget = await resolveConnectionDrop(event);
        const locationTarget = getLocationConnectionTarget(this.#locationId);
        if (!relatedTarget || !locationTarget) return;
        try {
            if (action === "owner") {
                if (relatedTarget.entityType !== "faction") return ui.notifications.warn("Drop a Nexus Faction here.");
                const current = getOwnership(locationTarget, { includeHidden: true });
                if (current && current.ownerNodeId !== relatedTarget.id) {
                    const confirmed = await this.#confirmOwnerTransfer(current, relatedTarget);
                    if (!confirmed) return;
                }
                const edge = await setOwner(locationTarget, relatedTarget);
                const updated = getOwnership(locationTarget, { includeHidden: true });
                if (!edge || updated?.ownerNodeId !== relatedTarget.id) throw new Error("The new political owner was not saved.");
            } else if (action === "leader") {
                if (relatedTarget.entityType !== "npc") return ui.notifications.warn("Drop a Nexus Person here.");
                const edge = await setLocationLeader(locationTarget, relatedTarget);
                const updated = getLocationLeader(locationTarget, { includeHidden: true });
                if (!edge || updated?.leaderNodeId !== relatedTarget.id) throw new Error("The new Location leader was not saved.");
            }
            this.#refresh();
        } catch (error) {
            console.error("Augur: Nexus | Failed to update settlement authority", error);
            ui.notifications.error(error?.message || "The Location authority could not be updated.");
        }
    }

    async #confirmOwnerTransfer(current, factionTarget) {
        const locationName = foundry.utils.escapeHTML(this.#entity?.display?.name || "this Location");
        const ownerName = foundry.utils.escapeHTML(current?.owner?.name || this.#overview?.owner?.name || "its current owner");
        const factionName = foundry.utils.escapeHTML(factionTarget?.name || "this Organization");
        return (await foundry.applications.api.DialogV2.wait({
            window: { title: "Transfer Location Ownership", icon: "fa-solid fa-shield-halved" },
            position: { width: 440 },
            content: `<div class="nexus-delete-confirm"><p><strong>${locationName}</strong> is currently owned by <strong>${ownerName}</strong>.</p><p>Transfer it to <strong>${factionName}</strong>?</p></div>`,
            rejectClose: false,
            modal: true,
            buttons: [
                { action: "confirm", label: "Transfer Holding", icon: "fa-solid fa-right-left", callback: () => true },
                { action: "cancel", label: "Cancel", icon: "fa-solid fa-xmark", default: true, callback: () => false }
            ]
        })) === true;
    }

    #toggleCompact() {
        const current = this.position || {};
        if (!this.#isCompact) this.#expandedPosition = { ...current };
        this.#isCompact = !this.#isCompact;
        compactPreference = this.#isCompact;
        if (this.#isCompact) {
            const width = Math.min(COMPACT_WIDTH, Math.max(320, window.innerWidth - 40));
            this.setPosition({ width, left: Math.max(20, (window.innerWidth - width) / 2) });
        } else {
            this.setPosition({ ...(this.#expandedPosition || {}), width: Number(this.#expandedPosition?.width || 1420) });
        }
        this.render({ parts: ["content"] });
    }

    #renderCompactExtension() {
        if (!this.#isCompact) return "";
        return foundry.applications.handlebars.renderTemplate(
            "modules/augur-nexus/templates/campaign-entities/settlement-compact-extension.hbs",
            {
                isGM: game.user?.isGM === true,
                overview: this.#overview,
                profileRows: LocationEntityModel.getProfileRows(this.#entity),
                cards: this.#cards,
                economyHtml: this.#economyHtml,
                hasEconomy: true,
                canManageTrade: true
            }
        );
    }

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