import { and, eq, inArray, sql } from "drizzle-orm";
import { companyMemberships, instanceUserRoles, principalPermissionGrants, } from "@zmeel/db";
export function accessService(db) {
    async function isInstanceAdmin(userId) {
        if (!userId)
            return false;
        const row = await db
            .select({ id: instanceUserRoles.id })
            .from(instanceUserRoles)
            .where(and(eq(instanceUserRoles.userId, userId), eq(instanceUserRoles.role, "instance_admin")))
            .then((rows) => rows[0] ?? null);
        return Boolean(row);
    }
    async function getMembership(companyId, principalType, principalId) {
        return db
            .select()
            .from(companyMemberships)
            .where(and(eq(companyMemberships.companyId, companyId), eq(companyMemberships.principalType, principalType), eq(companyMemberships.principalId, principalId)))
            .then((rows) => rows[0] ?? null);
    }
    async function hasPermission(companyId, principalType, principalId, permissionKey) {
        const membership = await getMembership(companyId, principalType, principalId);
        if (!membership || membership.status !== "active")
            return false;
        const grant = await db
            .select({ id: principalPermissionGrants.id })
            .from(principalPermissionGrants)
            .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, principalType), eq(principalPermissionGrants.principalId, principalId), eq(principalPermissionGrants.permissionKey, permissionKey)))
            .then((rows) => rows[0] ?? null);
        return Boolean(grant);
    }
    async function canUser(companyId, userId, permissionKey) {
        if (!userId)
            return false;
        if (await isInstanceAdmin(userId))
            return true;
        return hasPermission(companyId, "user", userId, permissionKey);
    }
    async function listMembers(companyId) {
        return db
            .select()
            .from(companyMemberships)
            .where(eq(companyMemberships.companyId, companyId))
            .orderBy(sql `${companyMemberships.createdAt} desc`);
    }
    async function listActiveUserMemberships(companyId) {
        return db
            .select()
            .from(companyMemberships)
            .where(and(eq(companyMemberships.companyId, companyId), eq(companyMemberships.principalType, "user"), eq(companyMemberships.status, "active")))
            .orderBy(sql `${companyMemberships.createdAt} asc`);
    }
    async function setMemberPermissions(companyId, memberId, grants, grantedByUserId) {
        const member = await db
            .select()
            .from(companyMemberships)
            .where(and(eq(companyMemberships.companyId, companyId), eq(companyMemberships.id, memberId)))
            .then((rows) => rows[0] ?? null);
        if (!member)
            return null;
        await db.transaction(async (tx) => {
            await tx
                .delete(principalPermissionGrants)
                .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, member.principalType), eq(principalPermissionGrants.principalId, member.principalId)));
            if (grants.length > 0) {
                await tx.insert(principalPermissionGrants).values(grants.map((grant) => ({
                    companyId,
                    principalType: member.principalType,
                    principalId: member.principalId,
                    permissionKey: grant.permissionKey,
                    scope: grant.scope ?? null,
                    grantedByUserId,
                    createdAt: new Date(),
                    updatedAt: new Date(),
                })));
            }
        });
        return member;
    }
    async function promoteInstanceAdmin(userId) {
        const existing = await db
            .select()
            .from(instanceUserRoles)
            .where(and(eq(instanceUserRoles.userId, userId), eq(instanceUserRoles.role, "instance_admin")))
            .then((rows) => rows[0] ?? null);
        if (existing)
            return existing;
        return db
            .insert(instanceUserRoles)
            .values({
            userId,
            role: "instance_admin",
        })
            .returning()
            .then((rows) => rows[0]);
    }
    async function demoteInstanceAdmin(userId) {
        return db
            .delete(instanceUserRoles)
            .where(and(eq(instanceUserRoles.userId, userId), eq(instanceUserRoles.role, "instance_admin")))
            .returning()
            .then((rows) => rows[0] ?? null);
    }
    async function listUserCompanyAccess(userId) {
        return db
            .select()
            .from(companyMemberships)
            .where(and(eq(companyMemberships.principalType, "user"), eq(companyMemberships.principalId, userId)))
            .orderBy(sql `${companyMemberships.createdAt} desc`);
    }
    async function setUserCompanyAccess(userId, companyIds) {
        const existing = await listUserCompanyAccess(userId);
        const existingByCompany = new Map(existing.map((row) => [row.companyId, row]));
        const target = new Set(companyIds);
        await db.transaction(async (tx) => {
            const toDelete = existing.filter((row) => !target.has(row.companyId)).map((row) => row.id);
            if (toDelete.length > 0) {
                await tx.delete(companyMemberships).where(inArray(companyMemberships.id, toDelete));
            }
            for (const companyId of target) {
                if (existingByCompany.has(companyId))
                    continue;
                await tx.insert(companyMemberships).values({
                    companyId,
                    principalType: "user",
                    principalId: userId,
                    status: "active",
                    membershipRole: "member",
                });
            }
        });
        return listUserCompanyAccess(userId);
    }
    async function ensureMembership(companyId, principalType, principalId, membershipRole = "member", status = "active") {
        const existing = await getMembership(companyId, principalType, principalId);
        if (existing) {
            if (existing.status !== status || existing.membershipRole !== membershipRole) {
                const updated = await db
                    .update(companyMemberships)
                    .set({ status, membershipRole, updatedAt: new Date() })
                    .where(eq(companyMemberships.id, existing.id))
                    .returning()
                    .then((rows) => rows[0] ?? null);
                return updated ?? existing;
            }
            return existing;
        }
        return db
            .insert(companyMemberships)
            .values({
            companyId,
            principalType,
            principalId,
            status,
            membershipRole,
        })
            .returning()
            .then((rows) => rows[0]);
    }
    async function setPrincipalGrants(companyId, principalType, principalId, grants, grantedByUserId) {
        await db.transaction(async (tx) => {
            await tx
                .delete(principalPermissionGrants)
                .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, principalType), eq(principalPermissionGrants.principalId, principalId)));
            if (grants.length === 0)
                return;
            await tx.insert(principalPermissionGrants).values(grants.map((grant) => ({
                companyId,
                principalType,
                principalId,
                permissionKey: grant.permissionKey,
                scope: grant.scope ?? null,
                grantedByUserId,
                createdAt: new Date(),
                updatedAt: new Date(),
            })));
        });
    }
    async function copyActiveUserMemberships(sourceCompanyId, targetCompanyId) {
        const sourceMemberships = await listActiveUserMemberships(sourceCompanyId);
        for (const membership of sourceMemberships) {
            await ensureMembership(targetCompanyId, "user", membership.principalId, membership.membershipRole, "active");
        }
        return sourceMemberships;
    }
    async function listPrincipalGrants(companyId, principalType, principalId) {
        return db
            .select()
            .from(principalPermissionGrants)
            .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, principalType), eq(principalPermissionGrants.principalId, principalId)))
            .orderBy(principalPermissionGrants.permissionKey);
    }
    async function setPrincipalPermission(companyId, principalType, principalId, permissionKey, enabled, grantedByUserId, scope = null) {
        if (!enabled) {
            await db
                .delete(principalPermissionGrants)
                .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, principalType), eq(principalPermissionGrants.principalId, principalId), eq(principalPermissionGrants.permissionKey, permissionKey)));
            return;
        }
        await ensureMembership(companyId, principalType, principalId, "member", "active");
        const existing = await db
            .select()
            .from(principalPermissionGrants)
            .where(and(eq(principalPermissionGrants.companyId, companyId), eq(principalPermissionGrants.principalType, principalType), eq(principalPermissionGrants.principalId, principalId), eq(principalPermissionGrants.permissionKey, permissionKey)))
            .then((rows) => rows[0] ?? null);
        if (existing) {
            await db
                .update(principalPermissionGrants)
                .set({
                scope,
                grantedByUserId,
                updatedAt: new Date(),
            })
                .where(eq(principalPermissionGrants.id, existing.id));
            return;
        }
        await db.insert(principalPermissionGrants).values({
            companyId,
            principalType,
            principalId,
            permissionKey,
            scope,
            grantedByUserId,
            createdAt: new Date(),
            updatedAt: new Date(),
        });
    }
    return {
        isInstanceAdmin,
        canUser,
        hasPermission,
        getMembership,
        ensureMembership,
        listMembers,
        listActiveUserMemberships,
        copyActiveUserMemberships,
        setMemberPermissions,
        promoteInstanceAdmin,
        demoteInstanceAdmin,
        listUserCompanyAccess,
        setUserCompanyAccess,
        setPrincipalGrants,
        listPrincipalGrants,
        setPrincipalPermission,
    };
}
//# sourceMappingURL=access.js.map