import { FactionEntityModel } from "../../campaign-entities/models/FactionEntityModel.js";
import { CampaignEntityIndex } from "../../campaign-entities/services/CampaignEntityIndex.js";
import { OwnershipService } from "../../connections/services/OwnershipService.js";

const INDEPENDENT_BUCKET_ID = "independent";

export class PlaceFactionGroupingModel {
    static organize(rows = [], { collapsedGroupIds = new Set(), user = game.user } = {}) {
        const sourceRows = Array.isArray(rows) ? rows : [];
        const childrenByParent = new Map();
        for (const row of sourceRows) {
            const parentId = String(row.parentNodeId || "");
            const children = childrenByParent.get(parentId) || [];
            children.push(row);
            childrenByParent.set(parentId, children);
        }

        const result = [];
        const emitChildren = (actualParentId = "", viewParentId = "", depth = 0) => {
            const children = childrenByParent.get(actualParentId) || [];
            const locationChildren = children.filter(row => row.isLocation && !row.suppressFactionGrouping);
            const buckets = this.#buildBuckets(locationChildren, { collapsedGroupIds, user, actualParentId });
            const shouldGroup = buckets.length >= 2;
            let groupsEmitted = false;

            for (const child of children) {
                if (shouldGroup && child.isLocation && !child.suppressFactionGrouping) {
                    if (!groupsEmitted) {
                        groupsEmitted = true;
                        for (const bucket of buckets) {
                            const group = {
                                ...bucket,
                                viewParentNodeId: viewParentId,
                                viewDepth: depth,
                                indent: `${depth * 15}px`
                            };
                            result.push(group);
                            for (const location of bucket.rows) {
                                result.push({
                                    ...location,
                                    factionAccent: bucket.isIndependent ? "" : bucket.accent,
                                    hasFactionAccent: !bucket.isIndependent && !!bucket.accent,
                                    viewParentNodeId: group.id,
                                    viewDepth: depth + 1,
                                    indent: `${(depth + 1) * 15}px`
                                });
                                emitChildren(location.id, location.id, depth + 2);
                            }
                        }
                    }
                    continue;
                }

                result.push({
                    ...child,
                    viewParentNodeId: viewParentId,
                    viewDepth: depth,
                    indent: `${depth * 15}px`
                });
                emitChildren(child.id, child.id, depth + 1);
            }
        };

        emitChildren();
        return result;
    }

    static #buildBuckets(locationRows, { collapsedGroupIds, user, actualParentId }) {
        if (!locationRows.length) return [];
        const buckets = new Map();

        for (const row of locationRows) {
            const ownership = row.connectionTarget
                ? OwnershipService.getOwnership(row.connectionTarget, { user })
                : null;
            const factionId = ownership?.owner?.entityType === "faction"
                ? String(ownership.owner.entityId || "").trim()
                : "";
            const faction = factionId ? CampaignEntityIndex.getFaction(factionId) : null;
            const bucketId = faction ? factionId : INDEPENDENT_BUCKET_ID;
            let bucket = buckets.get(bucketId);
            if (!bucket) {
                const groupId = `nexus-location-faction-group:${actualParentId || "root"}:${bucketId}`;
                bucket = faction
                    ? {
                        id: groupId,
                        factionId,
                        name: FactionEntityModel.getName(faction),
                        subtitle: FactionEntityModel.getSubtitle(faction),
                        imageSrc: FactionEntityModel.getImage(faction),
                        accent: FactionEntityModel.getAccent(faction),
                        searchText: FactionEntityModel.getSearchText(faction),
                        isIndependent: false,
                        rows: [],
                        expanded: !collapsedGroupIds.has(groupId)
                    }
                    : {
                        id: groupId,
                        factionId: "",
                        name: "Independent",
                        subtitle: "No faction owner",
                        imageSrc: "",
                        accent: "",
                        searchText: "independent no faction owner",
                        isIndependent: true,
                        rows: [],
                        expanded: !collapsedGroupIds.has(groupId)
                    };
                buckets.set(bucketId, bucket);
            }
            bucket.rows.push(row);
        }

        return [...buckets.values()]
            .sort((left, right) => Number(left.isIndependent) - Number(right.isIndependent) || left.name.localeCompare(right.name))
            .map(bucket => ({
                ...bucket,
                isLocationFactionGroup: true,
                hasChildren: true,
                count: bucket.rows.length,
                viewParentNodeId: "",
                viewDepth: 0
            }));
    }
}
