const SCHEMA_VERSION = 2;
const TIERS = new Set(["member", "leader"]);

export class FactionMembershipModel {
    static SCHEMA_VERSION = SCHEMA_VERSION;
    static MEMBER_TIER = "member";
    static LEADER_TIER = "leader";

    static create(factionNode, memberNode, tier = this.MEMBER_TIER, { rank = null } = {}) {
        if (!this.canRepresent(factionNode, memberNode)) return null;
        const endpoints = this.resolveEndpoints(factionNode, memberNode);
        const membership = {
            schemaVersion: SCHEMA_VERSION,
            factionNodeId: endpoints.factionNode.id,
            memberNodeId: endpoints.memberNode.id,
            tier: this.normalizeTier(tier)
        };
        const normalizedRank = this.normalizeRank(rank);
        if (normalizedRank) membership.rank = normalizedRank;
        return membership;
    }

    static normalize(membership, { sourceNode = null, targetNode = null } = {}) {
        if (!membership || typeof membership !== "object" || Array.isArray(membership)) return null;
        if (!this.canRepresent(sourceNode, targetNode)) return null;
        return this.create(sourceNode, targetNode, membership.tier, { rank: membership.rank });
    }

    static canRepresent(leftNode = {}, rightNode = {}) {
        return !!this.resolveEndpoints(leftNode, rightNode);
    }

    static resolveEndpoints(leftNode = {}, rightNode = {}) {
        const leftFaction = this.#isEntityType(leftNode, "faction");
        const rightFaction = this.#isEntityType(rightNode, "faction");
        const leftNpc = this.#isEntityType(leftNode, "npc");
        const rightNpc = this.#isEntityType(rightNode, "npc");
        if (leftFaction && rightNpc) return { factionNode: leftNode, memberNode: rightNode };
        if (rightFaction && leftNpc) return { factionNode: rightNode, memberNode: leftNode };
        return null;
    }

    static isMembership(edge = {}) {
        return !!edge?.factionMembership && typeof edge.factionMembership === "object";
    }

    static normalizeTier(tier = this.MEMBER_TIER) {
        const value = String(tier || "").trim().toLocaleLowerCase();
        return TIERS.has(value) ? value : this.MEMBER_TIER;
    }

    static normalizeRank(rank = null) {
        if (!rank || typeof rank !== "object" || Array.isArray(rank)) return null;
        const id = String(rank.id || "").trim().toLocaleLowerCase().replace(/[^a-z0-9:.-]+/g, "-").replace(/^-+|-+$/g, "");
        const value = Number(rank.value);
        if (!id || !Number.isSafeInteger(value)) return null;
        const normalized = {
            id,
            value,
            label: String(rank.label || id).trim() || id,
            sourceModuleId: String(rank.sourceModuleId || "").trim()
        };
        if (!normalized.sourceModuleId) delete normalized.sourceModuleId;
        return normalized;
    }

    static isLeader(edge = {}) {
        return this.isMembership(edge) && edge.factionMembership.tier === this.LEADER_TIER;
    }

    static #isEntityType(node = {}, entityType = "") {
        return node?.kind === "nexus-entity" && node.entityType === entityType && !!node.id;
    }
}
