import { ConnectionStore } from "../../connections/services/ConnectionStore.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { LocationEntityModel } from "../models/LocationEntityModel.js";
import { LocationParentReference } from "../models/LocationParentReference.js";
import { PlaceParentService } from "../../places/services/PlaceParentService.js";
import { LocationEntityNormalizer } from "./LocationEntityNormalizer.js";
import { CampaignEntityIndex } from "./CampaignEntityIndex.js";

const MODULE_ID = "augur-nexus";

export class LocationEntityStore {
    static getLocations(options = {}) {
        return CampaignEntityIndex.getLocations(options);
    }

    static getLocation(idOrUuid) {
        return CampaignEntityIndex.getLocation(idOrUuid);
    }

    static resolveDocument(idOrUuid) {
        return CampaignEntityIndex.resolveLocationDocument(idOrUuid);
    }

    static async createLocation(data = {}) {
        const entity = await LocationEntityNormalizer.normalizeForCreate(data);
        this.#assertValidParent(entity);
        const folder = await this.#folder();
        const entry = await JournalEntry.create({
            name: LocationEntityModel.getName(entity),
            folder: folder?.id || null,
            ownership: { default: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER },
            flags: { [MODULE_ID]: { campaignEntity: entity } },
            pages: [this.#pageData()]
        });
        CampaignEntityIndex.upsert(entry);
        Hooks.callAll("augurNexusCampaignEntitiesChanged", { reason: "createLocation", entity, document: entry });
        return LocationEntityModel.fromDocument(entry);
    }

    static async updateLocation(id, patch = {}, options = {}) {
        const document = this.resolveDocument(id);
        if (!document) throw new Error("Location could not be found.");
        const current = LocationEntityModel.fromDocument(document);
        const entity = await LocationEntityNormalizer.normalizeForUpdate(current, patch, options);
        const transferSceneId = String(options.allowSceneParentTransfer || "").trim();
        const parent = entity.hierarchy?.parent || null;
        const isAuthorizedSceneTransfer = transferSceneId
            && parent?.kind === "scene"
            && parent.sceneId === transferSceneId
            && !!game.scenes.get(transferSceneId);
        if (!isAuthorizedSceneTransfer) this.#assertValidParent(entity);
        await document.update({
            name: LocationEntityModel.getName(entity),
            [`flags.${MODULE_ID}.campaignEntity`]: entity
        });
        CampaignEntityIndex.upsert(document);
        Hooks.callAll("augurNexusCampaignEntitiesChanged", {
            reason: "updateLocation",
            entity,
            document,
            syncMarkers: options.syncMarkers !== false
        });
        return LocationEntityModel.fromDocument(document);
    }

    static async deleteLocation(id, { confirmedOwnedScene = false, deleteOwnedScene = true } = {}) {
        const document = this.resolveDocument(id);
        if (!document) return false;
        const entity = LocationEntityModel.fromDocument(document);
        const primaryScene = game.scenes.get(entity.scenes?.primarySceneId);
        if (primaryScene && deleteOwnedScene) {
            const { NexusSceneDeletionCoordinator } = await import("../../nexus/services/NexusSceneDeletionCoordinator.js");
            const deleted = await NexusSceneDeletionCoordinator.deleteSceneBranch(primaryScene, {
                confirmed: confirmedOwnedScene,
                title: "Delete Location Scene",
                message: `Deleting <strong>${foundry.utils.escapeHTML(LocationEntityModel.getName(entity))}</strong> also deletes its owned Scene and everything nested beneath that Scene.`,
                confirmLabel: "Delete Location and Scene",
                intent: "delete-location-scene",
                source: "location-delete"
            });
            if (!deleted) return false;
        }
        const { NexusMarkerService } = await import("../../markers/services/NexusMarkerService.js");
        await NexusMarkerService.deleteMarkersForTarget({
            kind: "campaign-entity",
            entityType: "location",
            entityId: entity.id
        });
        const target = ConnectionTargetResolver.fromCampaignEntity(entity, document);
        if (target) await ConnectionStore.removeConnectionsForTarget(target);
        await document.delete();
        CampaignEntityIndex.remove(entity.id);
        Hooks.callAll("augurNexusCampaignEntitiesChanged", { reason: "deleteLocation", entity });
        return true;
    }

    static getJournalPage(id) {
        return this.resolveDocument(id)?.pages?.contents?.find(page => page.type === "text") || null;
    }

    static async getOrCreateJournalPage(id) {
        const document = this.resolveDocument(id);
        if (!document) return null;
        return this.getJournalPage(id) || this.#createPage(document);
    }

    static #assertValidParent(entity) {
        const validation = PlaceParentService.validate({
            childKey: `location:${entity.id}`,
            childType: "location",
            parentKey: LocationParentReference.toNodeKey(entity.hierarchy?.parent)
        });
        if (!validation.valid) throw new Error(validation.message || "Invalid location parent.");
    }

    static async #createPage(entry) {
        const [page] = await entry.createEmbeddedDocuments("JournalEntryPage", [this.#pageData()]);
        return page || null;
    }

    static #pageData() {
        return {
            name: "Journal",
            type: "text",
            sort: CONST.SORT_INTEGER_DENSITY,
            text: { content: "", format: 1 }
        };
    }

    static async #folder() {
        let parent = game.folders.find(folder =>
            folder.name === "Augur Campaign Entities" && folder.type === "JournalEntry" && !folder.folder
        );
        if (!parent) {
            parent = await Folder.create({
                name: "Augur Campaign Entities",
                type: "JournalEntry",
                color: "#1c3246"
            });
        }

        let folder = game.folders.find(candidate =>
            candidate.name === "Locations"
            && candidate.type === "JournalEntry"
            && candidate.folder?.id === parent.id
        );
        if (!folder) {
            folder = await Folder.create({
                name: "Locations",
                type: "JournalEntry",
                color: "#6a4728",
                folder: parent.id
            });
        }
        return folder;
    }
}
