import type { OrganizationStatus, Prisma } from "@prisma/client";

export class InactiveOrganizationError extends Error {
  readonly organizationId: string;
  readonly status: Exclude<OrganizationStatus, "ACTIVE">;

  constructor(organizationId: string, status: Exclude<OrganizationStatus, "ACTIVE">) {
    super(`organization ${organizationId} is ${status}; customer operations require ACTIVE`);
    this.name = "InactiveOrganizationError";
    this.organizationId = organizationId;
    this.status = status;
  }
}

export function inactiveOrganizationReason(
  organizationId: string,
  status: OrganizationStatus,
): string | undefined {
  if (status === "ACTIVE") return undefined;
  return `organization ${organizationId} is ${status}; customer operations require ACTIVE`;
}

export function requireActiveOrganizationStatus(
  organizationId: string,
  status: OrganizationStatus,
): void {
  if (status !== "ACTIVE") throw new InactiveOrganizationError(organizationId, status);
}

/**
 * Lock the Organization row for a customer operation and verify ACTIVE.
 * PostgreSQL UPDATE takes a conflicting row lock, so lifecycle transitions
 * serialize with the entire transaction that called this function.
 */
export async function lockActiveOrganization(
  prisma: Prisma.TransactionClient,
  organizationId: string,
): Promise<void> {
  const rows = await prisma.$queryRaw<Array<{ readonly id: string; readonly status: OrganizationStatus }>>`
    SELECT "id", "status"::text AS "status"
    FROM "Organization"
    WHERE "id" = ${organizationId}
    FOR SHARE
  `;
  const organization = rows[0];
  if (organization === undefined) throw new Error(`organization not found: ${organizationId}`);
  requireActiveOrganizationStatus(organization.id, organization.status);
}

/** Resolve a Project's tenant inside the transaction, then take the lifecycle lock. */
export async function lockActiveProjectOrganization(
  prisma: Prisma.TransactionClient,
  projectId: string,
): Promise<string> {
  const project = await prisma.project.findUnique({
    where: { id: projectId },
    select: { organizationId: true },
  });
  if (project === null) throw new Error(`project not found: ${projectId}`);
  await lockActiveOrganization(prisma, project.organizationId);
  return project.organizationId;
}
