import { ConnectionCategories } from "../../connections/services/ConnectionCategories.js";
import { ConnectionStore } from "../../connections/services/ConnectionStore.js";
import { ConnectionTargetResolver } from "../../connections/services/ConnectionTargetResolver.js";
import { ShipCaptainService } from "../../connections/services/ShipCaptainService.js";
import { ShipCrewService } from "../../connections/services/ShipCrewService.js";
import { ShipLocationService } from "../../connections/services/ShipLocationService.js";
import { ShipOwnershipService } from "../../connections/services/ShipOwnershipService.js";

export class ShipDomainViewModel {
    static fromEntity(entity = {}, options = {}) {
        const target = ConnectionTargetResolver.fromCampaignEntity(entity);
        const shipNodeId = ConnectionTargetResolver.getNodeId(target);
        if (!target || !shipNodeId) return this.#empty(options);
        const captainEntry = ShipCaptainService.getCaptain(target, options);
        const explicitOwner = ShipOwnershipService.getOwner(target, options);
        const automaticLocationTracking = ShipLocationService.isAutomaticTrackingEnabled();
        const location = ShipLocationService.getCurrentLocation(target, { ...options, allowMarkerFallback: automaticLocationTracking });
        const recordedCrew = ShipCrewService.getCrew(target, options);
        const captain = captainEntry ? { ...captainEntry.captain, edgeId: captainEntry.edgeId } : null;
        const legacyCrew = this.#legacyCaptainCrew(shipNodeId, captainEntry?.edgeId, recordedCrew, options);
        const crew = [...recordedCrew, ...legacyCrew].sort((left, right) => left.name.localeCompare(right.name));
        const owner = explicitOwner?.owner
            ? { ...explicitOwner.owner, edgeId: explicitOwner.edgeId, isDerived: false }
            : captain ? { ...captain, subtitle: "Captain-owned", isDerived: true } : null;

        const excludedEdges = new Set([
            captainEntry?.edgeId,
            explicitOwner?.edgeId,
            location?.edgeId,
            ...crew.map(entry => entry.edgeId)
        ].filter(Boolean));
        const worldTies = (ConnectionStore.getGraphContext().adjacency.get(shipNodeId) || [])
            .filter(({ edge }) => !excludedEdges.has(edge.id))
            .filter(({ edge, node }) => options.includeHidden || (ConnectionStore.canUserSeeConnection(edge, options.user || game.user) && ConnectionTargetResolver.canUserSeeNode(node, options.user || game.user)))
            .map(({ edge, node }) => this.#worldTie(edge, node, shipNodeId))
            .filter(Boolean)
            .sort((left, right) => left.name.localeCompare(right.name));

        return {
            canManage: options.isGM === true,
            owner,
            hasOwner: !!owner,
            hasExplicitOwner: !!explicitOwner,
            ownerLabel: explicitOwner ? "Owned by" : "Independent Ship",
            captain,
            hasCaptain: !!captain,
            location: location ? { ...location.place, edgeId: location.edgeId, source: location.source, adopted: location.adopted === true } : null,
            hasLocation: !!location,
            locationModeLabel: location?.source === "marker"
                ? (automaticLocationTracking ? "Tracked by Marker" : "Last Tracked Location")
                : "Recorded Manually",
            automaticLocationTracking,
            crew,
            hasCrew: crew.length > 0,
            crewCount: crew.length + (captain ? 1 : 0),
            worldTies,
            hasWorldTies: worldTies.length > 0,
            worldTieCount: worldTies.length
        };
    }

    static #worldTie(edge, node, shipNodeId) {
        const display = ConnectionTargetResolver.resolveDisplayNodeSync(node) || node;
        if (!display) return null;
        const categoryId = ConnectionStore.getConnectionCategoryForNode(edge, shipNodeId);
        const category = ConnectionCategories.get(categoryId);
        return {
            edgeId: edge.id,
            nodeId: node.id,
            name: display.name || "Unknown Connection",
            imageSrc: display.img || "",
            subtitle: display.subtitle || category?.singular || "Connection",
            role: ConnectionStore.getConnectionRoleForNode(edge, shipNodeId) || category?.singular || "Connection",
            categoryIcon: category?.icon || "fas fa-link",
            categoryColor: category?.color || "#55bdec",
            isFaction: node.kind === "nexus-entity" && node.entityType === "faction",
            isPerson: node.kind === "nexus-entity" && node.entityType === "npc",
            isShip: node.kind === "nexus-entity" && node.entityType === "ship",
            isPlace: node.kind === "nexus-site" || node.kind === "nexus-scene" || (node.kind === "nexus-entity" && node.entityType === "location")
        };
    }

    static #legacyCaptainCrew(shipNodeId, captainEdgeId, recordedCrew, options) {
        const excluded = new Set([captainEdgeId, ...recordedCrew.map(entry => entry.edgeId)].filter(Boolean));
        const recordedNodeIds = new Set(recordedCrew.map(entry => entry.nodeId));
        return (ConnectionStore.getGraphContext().adjacency.get(shipNodeId) || [])
            .filter(({ edge, node }) => edge.relationType === "ship-captain"
                && !excluded.has(edge.id)
                && node.kind === "nexus-entity"
                && node.entityType === "npc"
                && !recordedNodeIds.has(node.id))
            .filter(({ edge, node }) => options.includeHidden || (ConnectionStore.canUserSeeConnection(edge, options.user || game.user) && ConnectionTargetResolver.canUserSeeNode(node, options.user || game.user)))
            .map(({ edge, node }) => {
                const display = ConnectionTargetResolver.resolveDisplayNodeSync(node) || node;
                return {
                    edgeId: edge.id,
                    nodeId: node.id,
                    name: display.name || "Unknown Crew Member",
                    imageSrc: display.img || "",
                    subtitle: "Crew"
                };
            });
    }

    static #empty(options = {}) {
        return {
            canManage: options.isGM === true,
            owner: null,
            hasOwner: false,
            hasExplicitOwner: false,
            ownerLabel: "Independent Ship",
            captain: null,
            hasCaptain: false,
            location: null,
            hasLocation: false,
            locationModeLabel: "Manual Location Mode",
            automaticLocationTracking: false,
            crew: [],
            hasCrew: false,
            crewCount: 0,
            worldTies: [],
            hasWorldTies: false,
            worldTieCount: 0
        };
    }
}
