// Scene lifecycle hooks for Nexus. This owns browser refresh, branch delete interception, and post-delete cleanup.

import { NexusLineageManager } from "../services/NexusLineageManager.js";
import { NexusBrowserRefreshScheduler } from "../services/NexusBrowserRefreshScheduler.js";
import { NexusSceneDeletionCoordinator } from "../services/NexusSceneDeletionCoordinator.js";
import { NexusSceneOperations } from "../services/NexusSceneOperations.js";
import { SiteSceneVisibilityManager } from "../../site/services/SiteSceneVisibilityManager.js";
import { NexusMarkerService } from "../../markers/services/NexusMarkerService.js";
import { CampaignEntityJournalStore } from "../../campaign-entities/services/CampaignEntityJournalStore.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { ShipLocationService } from "../../connections/services/ShipLocationService.js";

const MODULE_ID = "augur-nexus";
const SCIFI_ID = "augur-scifi";

function refreshNexusBrowsers(reason = "scene-hooks") {
    NexusBrowserRefreshScheduler.schedule(reason);
}

let repairingBrokenLineage = false;
let pendingLineageRefreshReason = "";

function repairBrokenLineageAndRefresh(reason = "lineage") {
    if (repairingBrokenLineage) {
        NexusLineageManager.invalidateCache(reason);
        pendingLineageRefreshReason = reason;
        return;
    }

    const brokenScenes = NexusLineageManager.getBrokenLineageScenes();
    if (!brokenScenes.length) {
        invalidateLineageAndRefresh(reason);
        return;
    }

    repairingBrokenLineage = true;
    NexusLineageManager.repairBrokenLineage()
        .then(repairedScenes => {
            if (repairedScenes.length) {
                console.warn(`Augur Nexus | Repaired broken scene lineage for: ${repairedScenes.map(scene => scene.name).join(", ")}`);
                ui.notifications?.warn?.(`Augur Nexus repaired broken scene lineage for ${repairedScenes.length} scene${repairedScenes.length === 1 ? "" : "s"}.`);
            }
        })
        .catch(err => {
            console.error("Augur Nexus | Failed to repair broken scene lineage.", err);
            ui.notifications?.error?.("Augur Nexus could not repair broken scene lineage.");
        })
        .finally(() => {
            repairingBrokenLineage = false;
            const refreshReason = pendingLineageRefreshReason || reason;
            pendingLineageRefreshReason = "";
            invalidateLineageAndRefresh(`${refreshReason}-repair`);
        });
}

function invalidateLineageAndRefresh(reason = "lineage") {
    NexusLineageManager.invalidateCache(reason);
    refreshNexusBrowsers(reason);
}

function isNexusSiteDocument(document) {
    return !!document?.flags?.[MODULE_ID]?.site || !!document?.flags?.[MODULE_ID]?.sitePage;
}

function isNexusMapMarkerDocument(document) {
    const flags = document?.flags?.[MODULE_ID] || {};
    return !!flags.site || !!flags.sitePage || !!flags.siteLabel || !!flags.marker || !!flags.markerLabel;
}

function isAllowedVisualKey(key, allowedKeys, allowedPrefixes) {
    return allowedKeys.has(key) || allowedPrefixes.some(prefix => key.startsWith(prefix));
}

function isVisualOnlyNexusMarkerUpdate(document, data, options) {
    if (options?.[MODULE_ID]?.visualOnly) return true;
    if (!isNexusMapMarkerDocument(document)) return false;

    const flattened = foundry.utils.flattenObject(data || {});
    const keys = Object.keys(flattened);
    if (!keys.length) return false;

    const documentName = document?.documentName || "";
    if (documentName === "Tile") {
        const allowedKeys = new Set([
            "_id",
            "x",
            "y",
            "width",
            "height",
            "rotation",
            "hidden",
            "alpha",
            "texture.scaleX",
            "texture.scaleY"
        ]);
        const allowedPrefixes = [
            `flags.${MODULE_ID}.marker.placement.`,
            `flags.${MODULE_ID}.marker.presentation.showLabel`,
            `flags.${MODULE_ID}.marker.presentation.labelColor`,
            `flags.${MODULE_ID}.marker.presentation.labelFontSize`,
            `flags.${MODULE_ID}.marker.presentation.labelFontFamily`,
            `flags.${MODULE_ID}.marker.label.`
        ];
        return keys.every(key => isAllowedVisualKey(key, allowedKeys, allowedPrefixes));
    }

    if (documentName === "Drawing") {
        const allowedKeys = new Set([
            "_id",
            "x",
            "y",
            "text",
            "fontSize",
            "fontFamily",
            "textColor",
            "fillColor",
            "fillAlpha",
            "textAlpha",
            "hidden"
        ]);
        const allowedPrefixes = [
            "shape.",
            `flags.${MODULE_ID}.markerLabel.`,
            `flags.${MODULE_ID}.siteLabel.`,
            `flags.${MODULE_ID}.siteId`,
            `flags.${MODULE_ID}.ownerTileId`,
            `flags.${MODULE_ID}.markerId`,
            `flags.${MODULE_ID}.target`,
            `flags.${MODULE_ID}.labelRole`
        ];
        return keys.every(key => isAllowedVisualKey(key, allowedKeys, allowedPrefixes));
    }

    return false;
}

function isQuietScifiAnimationTileUpdate(data, options) {
    if (!options?.[SCIFI_ID]?.animationSync) return false;

    const flattened = foundry.utils.flattenObject(data || {});
    const allowedKeys = new Set([
        "_id",
        "x",
        "y",
        `flags.${SCIFI_ID}.orbit.phase`,
        `flags.${SCIFI_ID}.orbit.angle`
    ]);

    return Object.keys(flattened).every(key => allowedKeys.has(key));
}

function isNexusBrowserRelevantSceneUpdate(data = {}) {
    const keys = Object.keys(foundry.utils.flattenObject(data || {}))
        .filter(key => key !== "_id");
    if (!keys.length) return false;
    if (keys.every(key => key === `flags.${SCIFI_ID}.system.animating`)) return false;

    const exactKeys = new Set([
        "name",
        "sort",
        "thumb",
        "active",
        "navigation"
    ]);
    const prefixes = [
        "ownership.",
        `flags.${MODULE_ID}.nexusRoot`,
        `flags.${MODULE_ID}.lineage`,
        `flags.${MODULE_ID}.site`,
        `flags.${MODULE_ID}.placePreview`,
        `flags.${MODULE_ID}.playerAccess`,
        `flags.${SCIFI_ID}.journalId`,
        `flags.${SCIFI_ID}.planetScenes`,
        `flags.${SCIFI_ID}.moonScenes`,
        `flags.${SCIFI_ID}.planetScene`,
        `flags.${SCIFI_ID}.moonScene`,
        `flags.${SCIFI_ID}.siteScene`,
        `flags.${SCIFI_ID}.system`,
        `flags.${SCIFI_ID}.backTarget`
    ];

    return keys.some(key => exactKeys.has(key) || prefixes.some(prefix => key === prefix || key.startsWith(`${prefix}.`)));
}

export function registerSceneHooks() {
    Hooks.on("canvasReady", () => repairBrokenLineageAndRefresh("canvas-ready"));

    for (const hookName of ["createScene", "deleteScene"]) {
        Hooks.on(hookName, () => repairBrokenLineageAndRefresh(hookName));
    }
    Hooks.on("updateScene", (_scene, data) => {
        if (isNexusBrowserRelevantSceneUpdate(data)) repairBrokenLineageAndRefresh("updateScene");
    });

    Hooks.on("augurNexusLineageChanged", () => repairBrokenLineageAndRefresh("lineage-changed"));
    Hooks.on("augurNexusPlayerSceneAccessChanged", scene => {
        if (!scene?.id) return;
        SiteSceneVisibilityManager.applyMarkerVisibilityForTarget({
            kind: "nexus-scene",
            sceneId: scene.id
        }).catch(err => {
            console.warn("Augur Nexus | Failed to sync scene marker visibility.", err);
        });
        const siteFlags = scene.getFlag?.(MODULE_ID, "site") || {};
        const lineage = scene.getFlag?.(MODULE_ID, NexusLineageManager.LINEAGE_FLAG) || {};
        const parentSceneId = siteFlags.parentSceneId || lineage.parentSceneId || null;
        const siteId = siteFlags.siteId || lineage.parentSiteId || null;
        if (parentSceneId && siteId) {
            SiteSceneVisibilityManager.applyMarkerVisibilityForTarget({
                kind: "nexus-site",
                parentSceneId,
                siteId
            }).catch(err => {
                console.warn("Augur Nexus | Failed to sync linked site marker visibility.", err);
            });
        }
    });

    for (const hookName of ["createNote", "updateNote", "deleteNote", "createTile", "updateTile", "deleteTile", "createDrawing", "updateDrawing", "deleteDrawing", "createJournalEntryPage", "updateJournalEntryPage", "deleteJournalEntryPage"]) {
        Hooks.on(hookName, (document, data, options) => {
            if (hookName === "updateTile" && isQuietScifiAnimationTileUpdate(data, options)) return;
            if ((hookName === "updateNote" || hookName === "updateTile" || hookName === "updateDrawing") && isVisualOnlyNexusMarkerUpdate(document, data, options)) return;
            if (isNexusMapMarkerDocument(document)) invalidateLineageAndRefresh(hookName);
        });
    }

    Hooks.on("deleteTile", (document, options, userId) => {
        if (userId !== game.user.id
            || options?.[MODULE_ID]?.preserveShipLocation
            || options?.[MODULE_ID]?.managedMarkerDelete) return;
        const record = NexusMarkerService.getMarkerRecord(document);
        const target = record?.target || {};
        if (target.kind !== "campaign-entity" || target.entityType !== "ship" || !target.entityId) return;
        const ship = CampaignEntityJournalStore.getShip(target.entityId);
        const shipTarget = ship ? ConnectionTargetResolver.fromCampaignEntity(ship) : null;
        if (!shipTarget) return;
        const sceneTarget = document.parent ? ConnectionTargetResolver.fromSceneReference({ sceneId: document.parent.id }) : null;
        NexusMarkerService.removeOwnerMarkerRef(target, record.markerId)
            .then(() => ShipLocationService.isAutomaticTrackingEnabled()
                ? ShipLocationService.clearCurrentLocation(shipTarget, { markerOnly: true, markerId: record.markerId })
                : false)
            .then(() => ShipLocationService.isAutomaticTrackingEnabled() && sceneTarget
                ? ShipLocationService.removeLegacyMarkerSceneConnection(shipTarget, sceneTarget)
                : false)
            .then(() => Hooks.callAll("augurNexusCampaignEntitiesChanged", { reason: "shipMarkerDeleted", entity: ship }))
            .catch(error => console.warn("Augur Nexus | Failed to reconcile a deleted Ship marker.", error));
    });

    Hooks.on("preDeleteScene", (scene, options, userId) => {
        if (userId !== game.user.id) return;
        if (options?.[MODULE_ID]?.nexusDeleteHandled) return;

        const impact = NexusSceneDeletionCoordinator.getSceneBranchDeleteImpact(scene, {
            source: "foundry",
            intent: "delete-scene"
        });
        const plan = impact?.plan || null;
        const hasManagedImpact = Number(impact?.sceneCount || 0) > 1
            || Number(impact?.noteCount || 0) > 0
            || Number(impact?.pageCount || 0) > 0
            || Number(impact?.journalEntryCount || 0) > 0
            || (impact?.participantImpacts || []).some(row => Number(row?.count || 0) > 0)
            || !!plan?.locations?.length
            || !!plan?.shops?.length
            || !!plan?.entityCandidates?.length;
        if (!hasManagedImpact) return;

        NexusSceneDeletionCoordinator.deleteSceneBranch(scene, { plan }).catch(err => {
            console.error(err);
            ui.notifications.error("Failed to delete the selected Nexus branch.");
        });

        return false;
    });

    Hooks.on("deleteScene", (scene, options, userId) => {
        if (userId !== game.user.id) return;
        if (options?.[MODULE_ID]?.nexusDeleteHandled) return;

        NexusSceneOperations.cleanupDeletedScene(scene).catch(err => {
            console.error(err);
            ui.notifications.error("Failed to clean Nexus records for the deleted scene.");
        });
    });
}

