import { FactionSeatModel } from "../models/FactionSeatModel.js";
import { ConnectionStore } from "./ConnectionStore.js";
import { ConnectionTargetResolver } from "./ConnectionTargetResolver.js";
import { OwnershipService } from "./OwnershipService.js";

export class FactionSeatService {
    static canSetSeat(leftTarget, rightTarget) {
        const endpoints = this.#resolveTargetEndpoints(leftTarget, rightTarget);
        if (!endpoints) return false;
        const ownership = OwnershipService.getOwnership(endpoints.placeNode, { includeHidden: true });
        return ownership?.ownerNodeId === endpoints.factionNode.id;
    }

    static isSeatEdge(edgeOrId) {
        const edge = typeof edgeOrId === "string" ? ConnectionStore.getConnectionEdge(edgeOrId) : edgeOrId;
        return FactionSeatModel.isSeat(edge);
    }

    static getSeat(factionTarget, { user = game.user, includeHidden = false } = {}) {
        const factionNodeId = ConnectionTargetResolver.getNodeId(factionTarget);
        const entry = this.#getSeatEntries(factionNodeId)
            .find(candidate => candidate.seat.factionNodeId === factionNodeId);
        if (!entry) return null;
        if (!includeHidden && !ConnectionStore.canUserSeeConnection(entry.edge, user)) return null;
        if (!includeHidden && !ConnectionTargetResolver.canUserSeeNode(entry.otherNode, user)) return null;
        const display = ConnectionTargetResolver.resolveDisplayNodeSync(entry.otherNode) || entry.otherNode;
        return {
            edgeId: entry.edge.id,
            edge: entry.edge,
            factionNodeId,
            placeNodeId: entry.seat.placeNodeId,
            place: {
                id: entry.otherNode.id,
                entityId: entry.otherNode.entityId || "",
                name: display.name || "Unknown Seat",
                imageSrc: display.img || "",
                subtitle: display.subtitle || ""
            }
        };
    }

    static async setSeat(factionTarget, placeTarget, { edgeId = "" } = {}) {
        if (!game.user?.isGM) return null;
        const endpoints = this.#resolveTargetEndpoints(factionTarget, placeTarget);
        if (!endpoints) throw new Error("A faction seat requires one Organization and one Place.");
        const ownership = OwnershipService.getOwnership(endpoints.placeNode, { includeHidden: true });
        if (!ownership || ownership.ownerNodeId !== endpoints.factionNode.id) {
            throw new Error("Only a Place owned by this Organization can be its seat.");
        }
        if (edgeId && edgeId !== ownership.edgeId) {
            throw new Error("The selected Connection is not this Organization's ownership of the Location.");
        }
        const seat = FactionSeatModel.create(endpoints.factionNode, endpoints.placeNode);

        return ConnectionStore.runBatch(async () => {
            for (const current of this.#getSeatEntries(endpoints.factionNode.id)) {
                if (current.seat.factionNodeId !== endpoints.factionNode.id || current.edge.id === ownership.edgeId) continue;
                await ConnectionStore.updateConnection(current.edge.id, { factionSeat: null });
            }
            return ConnectionStore.updateConnection(ownership.edgeId, { factionSeat: seat });
        }, { reason: "setFactionSeat" });
    }

    static async clearSeat(factionTarget, { edgeId = "" } = {}) {
        if (!game.user?.isGM) return false;
        if (edgeId) {
            const edge = ConnectionStore.getConnectionEdge(edgeId);
            if (!FactionSeatModel.isSeat(edge)) return false;
            await ConnectionStore.updateConnection(edgeId, { factionSeat: null });
            return true;
        }
        const current = this.getSeat(factionTarget, { includeHidden: true });
        if (!current) return false;
        await ConnectionStore.updateConnection(current.edgeId, { factionSeat: null });
        return true;
    }

    static async openSeat(factionTarget, options = {}) {
        const seat = this.getSeat(factionTarget, options);
        const node = seat ? ConnectionStore.getNode(seat.placeNodeId) : null;
        if (!node) return false;
        await ConnectionTargetResolver.openNode(node);
        return true;
    }

    static #getSeatEntries(nodeId = "") {
        if (!nodeId) return [];
        return (ConnectionStore.getGraphContext().adjacency.get(nodeId) || [])
            .map(({ edge, node }) => ({ edge, otherNode: node, seat: edge.factionSeat }))
            .filter(entry => !!entry.seat);
    }

    static #resolveTargetEndpoints(leftTarget, rightTarget) {
        const leftNode = this.#nodeLike(leftTarget);
        const rightNode = this.#nodeLike(rightTarget);
        return FactionSeatModel.resolveEndpoints(leftNode, rightNode);
    }

    static #nodeLike(target) {
        const id = ConnectionTargetResolver.getNodeId(target);
        if (!id) return null;
        return ConnectionStore.getNode(id) || { ...target, id };
    }
}
