import { NexusMarkerService } from "../../markers/services/NexusMarkerService.js";
import { ConnectionStore } from "../../connections/services/ConnectionStore.js";
import { CampaignEntityVisibilityManager } from "./CampaignEntityVisibilityManager.js";

const UPDATE_REASONS = new Set([
    "updateNpc",
    "updateShip",
    "updateFaction",
    "updateLocation",
    "factionEmblemRendered"
]);

export class CampaignEntityPlacementSync {
    static #ownershipSignatures = null;
    static #pendingOwnershipTargets = new Map();
    static #ownershipProjectionTimer = null;
    static #ownershipProjectionWrite = Promise.resolve();
    static #connectionsReady = false;
    static #campaignEntitiesReady = false;
    static #pendingOwnershipScene = null;

    static registerHooks() {
        Hooks.on("augurNexusCampaignEntitiesChanged", payload => {
            const entity = payload?.entity || this.#entityFromDocument(payload?.document);
            if (!entity?.id || !entity?.type) return;

            if (UPDATE_REASONS.has(payload?.reason) && payload?.syncMarkers !== false) {
                this.#syncMarkerProjection(entity).catch(err => {
                    console.warn("Augur Nexus | Failed to sync placed campaign entity markers.", err);
                });
            }

            if (entity.type === "faction") {
                this.#queueOwnershipProjection(this.#getFactionHoldingTargets(entity.id));
            }
        });

        Hooks.on("augurNexusCampaignEntityVisibilityChanged", payload => {
            const entity = payload?.entity || null;
            const entities = entity?.id && entity?.type
                ? [entity]
                : CampaignEntityVisibilityManager.getEntitiesForType(payload?.entityType || "npc");
            import("../../site/services/SiteSceneVisibilityManager.js").then(({ SiteSceneVisibilityManager }) => {
                for (const campaignEntity of entities) {
                    SiteSceneVisibilityManager.applyMarkerVisibilityForTarget(campaignEntity).catch(err => {
                        console.warn("Augur Nexus | Failed to sync campaign entity marker visibility.", err);
                    });
                }
            });

            if (!payload?.entityType || payload.entityType === "faction") {
                const targets = entity?.type === "faction"
                    ? this.#getFactionHoldingTargets(entity.id)
                    : this.#getFactionOwnedTargets();
                this.#queueOwnershipProjection(targets);
            }
        });

        Hooks.on("augurNexusConnectionsChanged", ({ graph, reason = "" } = {}) => {
            if (!game.user?.isGM) return;
            const next = this.#buildOwnershipSignatures(graph || ConnectionStore.getGraphContext().graph);
            if (!this.#ownershipSignatures || reason === "databaseReady") {
                this.#ownershipSignatures = next;
                return;
            }
            const previous = this.#ownershipSignatures;
            const changedTargets = new Map();
            for (const [nodeId, entry] of previous.entries()) {
                if (next.get(nodeId)?.signature !== entry.signature) changedTargets.set(nodeId, entry.target);
            }
            for (const [nodeId, entry] of next.entries()) {
                if (previous.get(nodeId)?.signature !== entry.signature) changedTargets.set(nodeId, entry.target);
            }
            this.#ownershipSignatures = next;
            this.#queueOwnershipProjection(changedTargets.values());
        });


        Hooks.on("augurNexusConnectionsReady", ({ graph } = {}) => {
            if (!game.user?.isGM) return;
            this.#connectionsReady = true;
            this.#ownershipSignatures = this.#buildOwnershipSignatures(graph || ConnectionStore.getGraphContext().graph);
            this.#reconcileStartupOwnershipLabels();
        });

        Hooks.on("augurNexusCampaignEntitiesReady", () => {
            if (!game.user?.isGM) return;
            this.#campaignEntitiesReady = true;
            this.#reconcileStartupOwnershipLabels();
        });

        Hooks.on("canvasReady", canvasInstance => {
            if (!game.user?.isGM) return;
            this.#pendingOwnershipScene = canvasInstance?.scene || null;
            this.#reconcileStartupOwnershipLabels();
        });
    }

    static #entityFromDocument(document) {
        return document?.getFlag?.("augur-nexus", "campaignEntity")
            || document?.flags?.["augur-nexus"]?.campaignEntity
            || null;
    }

    static #buildOwnershipSignatures(graph = {}) {
        const signatures = new Map();
        const nodes = graph?.nodes || {};
        const visibilityPolicy = ConnectionStore.getGlobalPlayerConnectionVisibilityPolicy();
        for (const edge of Object.values(graph?.edges || {})) {
            if (edge?.relationType !== "ownership") continue;
            const owner = nodes[edge.sourceNodeId];
            const place = nodes[edge.targetNodeId];
            if (owner?.kind !== "nexus-entity" || owner.entityType !== "faction") continue;
            const target = this.#ownershipTargetFromNode(place);
            if (!target) continue;
            signatures.set(place.id, {
                target,
                signature: [
                    owner.entityId || owner.id,
                    edge.playerVisibility || "inherit",
                    visibilityPolicy
                ].join("|")
            });
        }
        return signatures;
    }

    static #getFactionHoldingTargets(factionId = "") {
        const normalizedFactionId = String(factionId || "").trim();
        if (!normalizedFactionId) return [];
        const nodeId = `nexus-entity:${normalizedFactionId}`;
        return (ConnectionStore.getGraphContext().adjacency.get(nodeId) || [])
            .filter(({ edge }) => edge?.relationType === "ownership" && edge.sourceNodeId === nodeId)
            .map(({ node }) => this.#ownershipTargetFromNode(node))
            .filter(Boolean);
    }

    static #getFactionOwnedTargets() {
        return [...this.#buildOwnershipSignatures(ConnectionStore.getGraphContext().graph).values()]
            .map(entry => entry.target);
    }

    static #reconcileStartupOwnershipLabels() {
        if (!this.#connectionsReady || !this.#campaignEntitiesReady || !this.#pendingOwnershipScene) return;
        const scene = this.#pendingOwnershipScene;
        this.#pendingOwnershipScene = null;
        NexusMarkerService.syncOwnershipLabelsForScene(scene).catch(err => {
            console.warn("Augur Nexus | Failed to reconcile ownership marker labels for the active Scene.", err);
        });
    }

    static async #syncMarkerProjection(entity) {
        await NexusMarkerService.syncMarkersForTarget(entity);
        const { SiteSceneVisibilityManager } = await import("../../site/services/SiteSceneVisibilityManager.js");
        await SiteSceneVisibilityManager.applyMarkerVisibilityForTarget(entity);
    }

    static #queueOwnershipProjection(targets = []) {
        if (!game.user?.isGM) return;
        for (const target of targets || []) {
            const key = this.#ownershipTargetKey(target);
            if (key) this.#pendingOwnershipTargets.set(key, target);
        }
        if (!this.#pendingOwnershipTargets.size || this.#ownershipProjectionTimer) return;
        this.#ownershipProjectionTimer = setTimeout(() => {
            this.#ownershipProjectionTimer = null;
            const pending = [...this.#pendingOwnershipTargets.values()];
            this.#pendingOwnershipTargets.clear();
            this.#ownershipProjectionWrite = this.#ownershipProjectionWrite
                .then(() => NexusMarkerService.syncOwnershipLabelsForTargets(pending))
                .catch(err => {
                    console.warn("Augur Nexus | Failed to sync faction ownership marker labels.", err);
                });
        }, 50);
    }

    static #ownershipTargetFromNode(node = {}) {
        if (node.kind === "nexus-site" && node.parentSceneId && node.siteId) {
            return { kind: "nexus-site", parentSceneId: node.parentSceneId, siteId: node.siteId };
        }
        if (node.kind === "nexus-scene" && node.sceneId) {
            return { kind: "nexus-scene", sceneId: node.sceneId };
        }
        if (node.kind === "nexus-entity" && node.entityType === "location" && node.entityId) {
            return { kind: "campaign-entity", entityType: "location", entityId: node.entityId };
        }
        return null;
    }

    static #ownershipTargetKey(target = {}) {
        if (typeof target === "string") return target ? `nexus-entity:${target}` : "";
        if (target.kind === "nexus-site") return target.parentSceneId && target.siteId ? `nexus-site:${target.parentSceneId}:${target.siteId}` : "";
        if (target.kind === "nexus-scene") return target.sceneId ? `nexus-scene:${target.sceneId}` : "";
        if (target.kind === "campaign-entity" && target.entityType === "location") return target.entityId ? `nexus-entity:${target.entityId}` : "";
        return "";
    }
}
