// src/lib/sites/access-actions.ts — owner/admin assign which sites a member
// may access. Fail-closed: the caller must be owner/admin of the active org, the
// target must be a member of it, and only the active org's sites can be granted.
"use server";
import { and, eq, inArray } from "drizzle-orm";
import { getActiveOrgId } from "@/lib/active-org";
import { member } from "@/lib/db/auth-schema";
import { db } from "@/lib/db/client";
import { siteAccess, sites } from "@/lib/db/domain-schema";
import { hasFullSiteAccess } from "./access";

/** Guard: current user manages the org (owner/admin) AND the target user is a
 *  member of the active org. Returns the active org id. */
async function assertManagerAndMember(userId: string): Promise<string> {
  if (!(await hasFullSiteAccess())) {
    throw new Error("Forbidden");
  }
  const orgId = await getActiveOrgId();
  const [m] = await db
    .select({ id: member.id })
    .from(member)
    .where(and(eq(member.organizationId, orgId), eq(member.userId, userId)))
    .limit(1);
  if (!m) throw new Error("Not a member of this organization");
  return orgId;
}

/** Site ids of the active org currently granted to `userId`. */
export async function getMemberSiteIdsAction(
  userId: string,
): Promise<string[]> {
  const orgId = await assertManagerAndMember(userId);
  const rows = await db
    .select({ siteId: siteAccess.siteId })
    .from(siteAccess)
    .innerJoin(sites, eq(siteAccess.siteId, sites.id))
    .where(and(eq(siteAccess.userId, userId), eq(sites.orgId, orgId)));
  return rows.map((r) => r.siteId);
}

/** Replace a member's granted sites with `siteIds` (filtered to the active org's
 *  sites). Owner/admin only. */
export async function setMemberSiteAccessAction(
  userId: string,
  siteIds: string[],
): Promise<void> {
  const orgId = await assertManagerAndMember(userId);
  // The active org's site ids (the only ones grantable).
  const orgSiteIds = (
    await db.select({ id: sites.id }).from(sites).where(eq(sites.orgId, orgId))
  ).map((r) => r.id);
  const orgSet = new Set(orgSiteIds);
  const next = [...new Set(siteIds)].filter((id) => orgSet.has(id));

  await db.transaction(async (tx) => {
    if (orgSiteIds.length) {
      await tx
        .delete(siteAccess)
        .where(
          and(
            eq(siteAccess.userId, userId),
            inArray(siteAccess.siteId, orgSiteIds),
          ),
        );
    }
    if (next.length) {
      await tx
        .insert(siteAccess)
        .values(next.map((siteId) => ({ siteId, userId })))
        .onConflictDoNothing();
    }
  });
}
