import { CampaignEntityJournalStore } from "../../campaign-entities/services/CampaignEntityJournalStore.js";
import { LocationEntityStore } from "../../campaign-entities/services/LocationEntityStore.js";
import { ConnectionStore } from "../../connections/services/ConnectionStore.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { NexusMarkerService } from "../../markers/services/NexusMarkerService.js";
import { NexusSceneOperations } from "../../nexus/services/NexusSceneOperations.js";
import { RegionPopulationProjectionCleanup } from "../../region-population/services/RegionPopulationProjectionCleanup.js";
import { ShopStore } from "../../shops/services/ShopStore.js";
import { BranchLocalEntityDeletionPolicy } from "./BranchLocalEntityDeletionPolicy.js";

const MODULE_ID = "augur-nexus";
const JOURNAL_DELETE_CHUNK_SIZE = 50;

export class NexusDeletionExecutor {
    static async execute(plan = {}, {
        operation = {},
        deleteScenes = true,
        selectedEntityIds = [],
        onProgress = null
    } = {}) {
        const sceneIds = (plan.scenes || []).map(row => row.id);
        const currentEntityImpact = BranchLocalEntityDeletionPolicy.analyze({
            anchorNodeIds: plan.structuralNodeIds || [],
            sceneIds
        });
        const safeEntityIds = BranchLocalEntityDeletionPolicy.normalizeSelection(
            currentEntityImpact,
            selectedEntityIds
        );
        const deletedByType = { npc: 0, ship: 0, faction: 0 };
        const selectedEntities = (currentEntityImpact.candidates || [])
            .filter(row => safeEntityIds.has(row.id));
        const liveScenes = (plan.scenes || [])
            .map(row => game.scenes.get(row.id) || null)
            .filter(Boolean);
        const total = selectedEntities.length
            + (plan.shops?.length || 0)
            + (plan.locations?.length || 0)
            + (deleteScenes ? liveScenes.length : 0);
        onProgress?.({ total, message: "Preparing linked content for deletion..." });

        const journalTargets = this.#collectJournalTargets({ selectedEntities, shops: plan.shops, locations: plan.locations });
        await this.#deleteTargetMarkers(journalTargets, onProgress);
        const deletedTargets = await this.#deleteJournalTargets(journalTargets, operation);
        await ConnectionStore.removeNodes(deletedTargets.map(target => target.nodeId), {
            reason: "deletionCascade"
        });
        await RegionPopulationProjectionCleanup.reconcileManagedDeletion({
            entities: deletedTargets.filter(target => target.kind === "entity")
                .map(target => ({ id: target.id, entityType: target.entityType })),
            locationIds: deletedTargets.filter(target => target.kind === "location").map(target => target.id)
        });

        for (const target of deletedTargets) {
            if (target.kind === "entity" && Object.hasOwn(deletedByType, target.entityType)) {
                deletedByType[target.entityType] += 1;
            }
        }
        const deletedShopCount = deletedTargets.filter(target => target.kind === "shop").length;
        const deletedLocationCount = deletedTargets.filter(target => target.kind === "location").length;
        const deletedEntityIds = deletedTargets.filter(target => target.kind === "entity").map(target => target.id);
        if (deletedTargets.some(target => target.kind === "entity" || target.kind === "location")) {
            Hooks.callAll("augurNexusCampaignEntitiesChanged", {
                reason: "managedCascadeDelete",
                entities: deletedTargets.filter(target => target.kind === "entity" || target.kind === "location")
                    .map(target => target.entity)
            });
        }
        if (deletedShopCount) {
            Hooks.callAll("augurNexusShopsChanged", {
                reason: "managedCascadeDelete",
                shops: deletedTargets.filter(target => target.kind === "shop").map(target => target.entity)
            });
        }
        onProgress?.({ advance: journalTargets.length });

        if (deleteScenes && liveScenes.length) {
            const rootScene = plan.rootScene && game.scenes.get(plan.rootScene.id)
                || plan.sceneRootContexts?.map(context => game.scenes.get(context.rootScene?.id)).find(Boolean)
                || liveScenes.at(-1);
            await NexusSceneOperations.deleteSceneBranch(rootScene, operation, {
                branch: liveScenes,
                onProgress: update => onProgress?.(update)
            });
            onProgress?.({ advance: liveScenes.length });
        }

        return {
            deletedEntityCount: Object.values(deletedByType).reduce((sum, count) => sum + count, 0),
            deletedEntityIds,
            deletedNpcIds: deletedTargets.filter(target => target.kind === "entity" && target.entityType === "npc").map(target => target.id),
            deletedShipIds: deletedTargets.filter(target => target.kind === "entity" && target.entityType === "ship").map(target => target.id),
            deletedFactionIds: deletedTargets.filter(target => target.kind === "entity" && target.entityType === "faction").map(target => target.id),
            deletedNpcCount: deletedByType.npc,
            deletedShipCount: deletedByType.ship,
            deletedFactionCount: deletedByType.faction,
            preservedEntityCount: (currentEntityImpact.candidates?.length || 0) - safeEntityIds.size,
            deletedShopCount,
            deletedShopIds: deletedTargets.filter(target => target.kind === "shop").map(target => target.id),
            deletedLocationCount,
            deletedLocationIds: deletedTargets.filter(target => target.kind === "location").map(target => target.id),
            deletedSceneCount: deleteScenes ? liveScenes.length : 0
        };
    }

    static #collectJournalTargets({ selectedEntities = [], shops = [], locations = [] } = {}) {
        const targets = [];
        for (const row of selectedEntities || []) {
            const document = this.#resolveEntityDocument(row);
            if (!document) continue;
            targets.push({
                kind: "entity",
                id: row.id,
                entityType: row.entityType,
                entity: row.entity,
                document,
                nodeId: row.nodeId,
                markerTarget: { kind: "campaign-entity", entityType: row.entityType, entityId: row.id }
            });
        }
        for (const row of shops || []) {
            const document = ShopStore.resolveDocument(row.id);
            const target = document ? ConnectionTargetResolver.fromDocument(document, { category: "service" }) : null;
            if (!document) continue;
            targets.push({
                kind: "shop",
                id: row.id,
                entity: row.entity,
                document,
                nodeId: ConnectionTargetResolver.getNodeId(target),
                markerTarget: { kind: "nexus-shop", shopId: row.id }
            });
        }
        for (const row of locations || []) {
            const document = LocationEntityStore.resolveDocument(row.id);
            if (!document) continue;
            targets.push({
                kind: "location",
                id: row.id,
                entityType: "location",
                entity: row.entity,
                document,
                nodeId: ConnectionTargetResolver.getEntityNodeId(row.id),
                markerTarget: { kind: "campaign-entity", entityType: "location", entityId: row.id }
            });
        }
        return targets;
    }

    static async #deleteTargetMarkers(targets, onProgress) {
        if (!targets.length) return;
        onProgress?.({ message: `Preparing markers for ${targets.length} linked document${targets.length === 1 ? "" : "s"}...` });
        await NexusMarkerService.deleteMarkersForTargets(
            targets.map(target => target.markerTarget),
            { ownersWillBeDeleted: true }
        );
    }

    static async #deleteJournalTargets(targets, operation = {}) {
        const targetByDocumentId = new Map(targets.map(target => [target.document.id, target]));
        const deletedTargets = [];
        const deletionBatchId = foundry.utils.randomID();
        const managedOperation = foundry.utils.mergeObject(operation || {}, {
            [MODULE_ID]: { managedCascade: true, deletionBatchId }
        }, { inplace: false });

        for (let index = 0; index < targets.length; index += JOURNAL_DELETE_CHUNK_SIZE) {
            const chunk = targets.slice(index, index + JOURNAL_DELETE_CHUNK_SIZE);
            const deleted = await JournalEntry.deleteDocuments(chunk.map(target => target.document.id), managedOperation);
            for (const document of deleted || []) {
                const target = targetByDocumentId.get(document.id);
                if (target) deletedTargets.push(target);
            }
        }
        return deletedTargets;
    }

    static #resolveEntityDocument(row) {
        if (row.entityType === "npc") return CampaignEntityJournalStore.resolveNpcDocument(row.id);
        if (row.entityType === "ship") return CampaignEntityJournalStore.resolveShipDocument(row.id);
        if (row.entityType === "faction") return CampaignEntityJournalStore.resolveFactionDocument(row.id);
        return null;
    }

}
