// Shared picker for adopting an eligible world Scene into a Site or Location workflow.

import { NexusLineageManager } from "../../nexus/services/NexusLineageManager.js";
import { LegacySciFiCompatibility } from "../../../support/compatibility/LegacySciFiCompatibility.js";
import { LocationEntityStore } from "../../campaign-entities/services/LocationEntityStore.js";
import { PlaceHierarchyModel } from "../../places/models/PlaceHierarchyModel.js";
import { PlaceTypeRegistry } from "../../places/services/PlaceTypeRegistry.js";

const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;

export class SiteScenePicker extends HandlebarsApplicationMixin(ApplicationV2) {
    static DEFAULT_OPTIONS = {
        id: "augur-nexus-site-scene-picker",
        classes: ["augur-nexus", "site-scene-picker-dialog"],
        tag: "div",
        window: {
            title: "Select Scene",
            resizable: true,
            minimizable: false
        },
        position: {
            width: 420,
            height: 520
        }
    };

    static PARTS = {
        main: {
            template: "modules/augur-nexus/templates/site/site-scene-picker.hbs"
        }
    };

    constructor(currentSelection, onSelect, { mode = "site", excludedSceneIds = [], locationId = null } = {}) {
        super();
        this.currentSelection = currentSelection || null;
        this.onSelect = onSelect;
        this.mode = mode === "location" ? "location" : "site";
        this.locationId = String(locationId || "").trim() || null;
        this.includeEstablishedBranches = false;
        this.excludedSceneIds = new Set((excludedSceneIds || []).map(id => String(id || "").trim()).filter(Boolean));
    }

    async _prepareContext() {
        const currentSceneId = canvas.scene?.id || null;
        const rootSceneId = NexusLineageManager.getRootScene()?.id || null;
        const locationSceneIds = this.mode === "location"
            ? new Set(LocationEntityStore.getLocations().map(location => location.scenes?.primarySceneId).filter(Boolean))
            : new Set();
        const branchIndex = this.mode === "location"
            ? this.#buildBranchIndex(PlaceTypeRegistry.getNodes({ isGM: true, includeHidden: true }))
            : { descendantCounts: new Map(), containingSceneKeys: new Set() };
        const scenes = game.scenes.contents
            .map(scene => {
                const sceneKey = `scene:${scene.id}`;
                const descendantCount = branchIndex.descendantCounts.get(sceneKey) || 0;
                return {
                    scene,
                    descendantCount,
                    containsLocation: branchIndex.containingSceneKeys.has(sceneKey)
                };
            })
            .filter(candidate => this.#isAdoptableScene(candidate.scene, {
                currentSceneId,
                rootSceneId,
                locationSceneIds,
                excludedSceneIds: this.excludedSceneIds,
                excludeCurrentScene: this.mode !== "location",
                descendantCount: candidate.descendantCount,
                containsLocation: candidate.containsLocation
            }))
            .sort((left, right) => (left.scene.folder?.sort ?? 0) - (right.scene.folder?.sort ?? 0)
                || left.scene.sort - right.scene.sort
                || left.scene.name.localeCompare(right.scene.name))
            .map(({ scene, descendantCount }) => ({
                id: scene.id,
                name: scene.name,
                folderName: scene.folder?.name || "",
                thumb: scene.thumb || "",
                isCurrent: scene.id === currentSceneId,
                isActiveForPlayers: scene.active,
                isSelected: scene.id === this.currentSelection,
                descendantCount,
                hasDescendants: descendantCount > 0,
                descendantLabel: `${descendantCount} ${descendantCount === 1 ? "descendant" : "descendants"}`
            }));

        return {
            scenes,
            hasScenes: scenes.length > 0,
            emptyLabel: this.mode === "location" && !this.includeEstablishedBranches
                ? "No standalone Scenes found. Include established branches to see eligible roots with descendants."
                : "No eligible Scenes found.",
            showEstablishedBranchToggle: this.mode === "location",
            includeEstablishedBranches: this.includeEstablishedBranches,
            explainer: this.mode === "location"
                ? this.includeEstablishedBranches
                    ? "Eligible root Scene branches are shown. Site maps, linked Scenes, and branches containing this Location remain excluded."
                    : "Only standalone Scenes are shown. Enable established branches to include eligible roots with descendants."
                : ""
        };
    }

    #isAdoptableScene(scene, { currentSceneId = null, rootSceneId = null, locationSceneIds = new Set(), excludedSceneIds = new Set(), excludeCurrentScene = true, descendantCount = 0, containsLocation = false } = {}) {
        if (!scene?.id) return false;
        if (excludeCurrentScene && scene.id === currentSceneId) return false;
        if (excludedSceneIds.has(scene.id)) return false;
        if (containsLocation) return false;
        if (this.mode === "location" && descendantCount > 0 && !this.includeEstablishedBranches) return false;
        if (scene.id === rootSceneId) return false;
        if (locationSceneIds.has(scene.id)) return false;
        if (scene.getFlag("augur-nexus", NexusLineageManager.ROOT_FLAG) === true) return false;
        const lineage = scene.getFlag("augur-nexus", NexusLineageManager.LINEAGE_FLAG) || {};
        if (lineage.parentSceneId || lineage.parentSiteId) return false;
        if (scene.getFlag("augur-nexus", "navigation")?.parentSceneId) return false;
        if (scene.getFlag("augur-nexus", "siteScene") === true) return false;
        if (scene.getFlag("augur-nexus", "site")?.parentSceneId) return false;
        if (LegacySciFiCompatibility.getParentScene(scene)) return false;
        if (LegacySciFiCompatibility.getSceneKind(scene)) return false;
        return true;
    }

    #buildBranchIndex(nodes = []) {
        const rows = PlaceHierarchyModel.buildRows(nodes);
        const byKey = new Map(rows.map(row => [row.key, row]));
        const descendantCounts = new Map();

        for (const row of rows) {
            const visited = new Set();
            let parentKey = row.parentNodeId;
            while (parentKey && !visited.has(parentKey)) {
                visited.add(parentKey);
                descendantCounts.set(parentKey, (descendantCounts.get(parentKey) || 0) + 1);
                parentKey = byKey.get(parentKey)?.parentNodeId || null;
            }
        }

        const containingSceneKeys = new Set();
        const targetKey = this.locationId ? `location:${this.locationId}` : null;
        const visited = new Set();
        let parentKey = targetKey ? byKey.get(targetKey)?.parentNodeId || null : null;
        while (parentKey && !visited.has(parentKey)) {
            visited.add(parentKey);
            const parent = byKey.get(parentKey);
            if (parent?.type === "scene") containingSceneKeys.add(parent.key);
            parentKey = parent?.parentNodeId || null;
        }

        return { descendantCounts, containingSceneKeys };
    }
    _attachPartListeners(partId, htmlElement, options) {
        super._attachPartListeners(partId, htmlElement, options);
        const el = htmlElement instanceof HTMLElement ? htmlElement : htmlElement[0];
        if (!el) return;

        const branchToggle = el.querySelector("input[name='includeEstablishedBranches']");
        const searchInput = el.querySelector("input[name='sceneSearch']");
        const cards = [...el.querySelectorAll("[data-scene-id]")];

        const applyFilter = () => {
            const query = (searchInput?.value || "").trim().toLowerCase();
            for (const card of cards) {
                const haystack = `${card.dataset.sceneName || ""} ${card.dataset.sceneFolder || ""}`.toLowerCase();
                card.hidden = !!query && !haystack.includes(query);
            }
        };

        searchInput?.addEventListener("input", applyFilter);

        branchToggle?.addEventListener("change", event => {
            this.includeEstablishedBranches = !!event.currentTarget.checked;
            this.render({ parts: ["main"] });
        });

        for (const card of cards) {
            const choose = async () => {
                const sceneId = card.dataset.sceneId;
                if (!sceneId) return;
                const descendantCount = Number(card.dataset.descendantCount || 0);
                const accepted = await this.onSelect?.(sceneId, { descendantCount });
                if (accepted === false) return;
                await this.close();
            };

            card.addEventListener("click", () => void choose());
            card.addEventListener("keydown", event => {
                if (event.key !== "Enter" && event.key !== " ") return;
                event.preventDefault();
                void choose();
            });
        }
    }
}



