import { ConnectionSidebarModel } from "../../connections/services/ConnectionSidebarModel.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { ShipOwnershipService } from "../../connections/services/ShipOwnershipService.js";
import { FactionEntityModel } from "../models/FactionEntityModel.js";
import { ShipEntityModel } from "../models/ShipEntityModel.js";
import { CampaignEntityIndex } from "./CampaignEntityIndex.js";
import { CampaignEntityVisibilityManager } from "./CampaignEntityVisibilityManager.js";

export class ShipDirectoryModel {
    static build({ search = "", expandedNodeIds = new Set(), isGM = game.user.isGM } = {}) {
        const ships = CampaignEntityIndex.getShips({ search });
        const rows = ships.filter(entity => CampaignEntityVisibilityManager.canUserSee(entity, game.user)).map(entity => {
            const target = ConnectionTargetResolver.fromCampaignEntity(entity);
            const nodeId = ConnectionTargetResolver.getNodeId(target);
            const expanded = expandedNodeIds.has(nodeId);
            const connections = ConnectionSidebarModel.buildForTarget(target, { expanded });
            const isPlayerHidden = !CampaignEntityVisibilityManager.isVisibleToPlayers(entity);
            return {
                id: entity.id,
                entityId: entity.id,
                entityType: "ship",
                name: ShipEntityModel.getName(entity),
                subtitle: ShipEntityModel.getSubtitle(entity),
                searchText: ShipEntityModel.getSearchText(entity),
                imageSrc: ShipEntityModel.getImage(entity),
                placementImageSrc: ShipEntityModel.getImage(entity),
                accent: ShipEntityModel.getAccent(entity),
                sourceModule: entity.sourceModule || "",
                tags: entity.tags || [],
                journalEntryId: entity.journalEntryId || "",
                uuid: entity.uuid || "",
                connections,
                isPlayerHidden,
                playerVisibilityValue: CampaignEntityVisibilityManager.getPlayerVisibilityOverride(entity),
                playerVisibilityLabel: CampaignEntityVisibilityManager.getPlayerVisibilityLabel(entity),
                playerVisibilityIcon: CampaignEntityVisibilityManager.getPlayerVisibilityIcon(entity),
                hasOptions: isGM,
                canOpen: true
            };
        });
        return {
            rows,
            hasRows: rows.length > 0,
            isSearching: !!String(search || "").trim(),
            emptyLabel: String(search || "").trim() ? "No ships match this search." : "No ships have been created yet."
        };
    }

    static organizeByFaction(directory = {}, {
        grouped = true,
        collapsedFactionIds = new Set(),
        sceneFactionIds = null,
        user = game.user
    } = {}) {
        const sourceRows = Array.isArray(directory?.rows) ? directory.rows : [];
        if (!grouped) {
            return {
                ...directory,
                groups: [],
                grouped: false,
                hasGroups: false,
                hasCollapsibleGroups: false,
                allGroupsCollapsed: false
            };
        }

        const buckets = new Map();
        const independentRows = [];

        for (const faction of CampaignEntityIndex.getFactions()) {
            if (!CampaignEntityVisibilityManager.canUserSee(faction, user)) continue;
            const factionId = faction.id;
            buckets.set(factionId, {
                id: factionId,
                entityId: factionId,
                entityType: "faction",
                nodeId: ConnectionTargetResolver.getEntityNodeId(factionId),
                name: FactionEntityModel.getName(faction),
                subtitle: FactionEntityModel.getSubtitle(faction),
                imageSrc: FactionEntityModel.getImage(faction),
                placementImageSrc: FactionEntityModel.getImage(faction),
                accent: FactionEntityModel.getAccent(faction),
                searchText: FactionEntityModel.getSearchText(faction),
                expanded: !collapsedFactionIds.has(factionId),
                isUnaffiliated: false,
                isPlayerHidden: !CampaignEntityVisibilityManager.isVisibleToPlayers(faction),
                playerVisibilityLabel: CampaignEntityVisibilityManager.getPlayerVisibilityLabel(faction),
                playerVisibilityIcon: CampaignEntityVisibilityManager.getPlayerVisibilityIcon(faction),
                hasOptions: user?.isGM === true,
                rows: []
            });
        }

        for (const row of sourceRows) {
            const target = { id: row.connections?.nodeId || ConnectionTargetResolver.getEntityNodeId(row.entityId) };
            const ownership = ShipOwnershipService.getOwner(target, { user });
            const factionId = ownership?.owner?.entityType === "faction"
                ? String(ownership.owner.entityId || "").trim()
                : "";
            const bucket = factionId ? buckets.get(factionId) : null;
            if (bucket) bucket.rows.push({
                ...row,
                hasFactionAccent: true,
                factionAccent: bucket.accent
            });
            else independentRows.push(row);
        }

        const groups = [...buckets.values()]
            .filter(group => !(sceneFactionIds instanceof Set)
                || sceneFactionIds.has(group.id)
                || group.rows.length > 0)
            .sort((a, b) => a.name.localeCompare(b.name))
            .map(group => ({
                ...group,
                count: group.rows.length,
                hasMembers: group.rows.length > 0,
                expanded: group.rows.length > 0 && group.expanded
            }));
        if (independentRows.length) {
            groups.push({
                id: "independent",
                nodeId: "",
                name: "Independent",
                subtitle: "No organization owner",
                imageSrc: "",
                accent: "",
                searchText: "independent no organization owner",
                expanded: !collapsedFactionIds.has("independent"),
                isUnaffiliated: true,
                rows: independentRows,
                count: independentRows.length,
                hasMembers: true
            });
        }

        return {
            ...directory,
            rows: sourceRows,
            groups,
            grouped: true,
            hasGroups: groups.length > 0,
            hasCollapsibleGroups: groups.some(group => group.hasMembers),
            allGroupsCollapsed: groups.some(group => group.hasMembers)
                && groups.filter(group => group.hasMembers).every(group => !group.expanded),
            hasRows: sourceRows.length > 0 || groups.length > 0,
            emptyLabel: sourceRows.length || groups.length
                ? directory.emptyLabel
                : "No ships or organizations have been created yet."
        };
    }
}
