/**
 * Google Cloud IAM driver — service-account creation + key minting.
 *
 * Auth uses the shared `vibn-workspace-provisioner` SA via getGcpAccessToken().
 * That SA needs `roles/iam.serviceAccountAdmin` and `roles/iam.serviceAccountKeyAdmin`
 * at the project level, plus `roles/iam.serviceAccountUser` so it can act as the
 * SAs it creates.
 *
 * All calls go through https://iam.googleapis.com/v1.
 */

import { getGcpAccessToken, GCP_PROJECT_ID } from '@/lib/gcp-auth';

const IAM_API = 'https://iam.googleapis.com/v1';

async function authedFetch(
  method: 'GET' | 'POST' | 'DELETE' | 'PATCH',
  url: string,
  body?: unknown,
): Promise<Response> {
  const token = await getGcpAccessToken();
  const headers: Record<string, string> = {
    Authorization: `Bearer ${token}`,
    Accept: 'application/json',
  };
  if (body) headers['Content-Type'] = 'application/json';
  return fetch(url, {
    method,
    headers,
    body: body ? JSON.stringify(body) : undefined,
  });
}

async function parseOrThrow<T>(res: Response, context: string): Promise<T> {
  const text = await res.text();
  if (!res.ok) {
    throw new Error(`[gcp-iam ${context} ${res.status}] ${text.slice(0, 500)}`);
  }
  return text ? (JSON.parse(text) as T) : ({} as T);
}

// ────────────────────────────────────────────────────────────────────
// Service-account naming
// ────────────────────────────────────────────────────────────────────

/**
 * GCP service-account IDs are 6-30 chars, [a-z][a-z0-9-]{4,28}[a-z0-9].
 * Some workspace slugs are too long or have edge characters, so normalize.
 */
export function workspaceServiceAccountId(slug: string): string {
  const safe = slug.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
  // Reserve "vibn-ws-" prefix (8 chars) → up to 22 left for the slug.
  const trimmed = safe.replace(/^-+|-+$/g, '').slice(0, 22) || 'workspace';
  const padded = trimmed.length < 4 ? `${trimmed}-ws` : trimmed;
  return `vibn-ws-${padded}`;
}

export function workspaceServiceAccountEmail(slug: string, projectId = GCP_PROJECT_ID): string {
  return `${workspaceServiceAccountId(slug)}@${projectId}.iam.gserviceaccount.com`;
}

// ────────────────────────────────────────────────────────────────────
// Service-account CRUD
// ────────────────────────────────────────────────────────────────────

export interface GcpServiceAccount {
  name: string;
  email: string;
  uniqueId: string;
  displayName?: string;
  description?: string;
}

export async function getServiceAccount(email: string): Promise<GcpServiceAccount | null> {
  const url = `${IAM_API}/projects/${GCP_PROJECT_ID}/serviceAccounts/${encodeURIComponent(email)}`;
  const res = await authedFetch('GET', url);
  if (res.status === 404) return null;
  return parseOrThrow<GcpServiceAccount>(res, 'getServiceAccount');
}

export async function createServiceAccount(opts: {
  accountId: string;
  displayName: string;
  description?: string;
}): Promise<GcpServiceAccount> {
  const url = `${IAM_API}/projects/${GCP_PROJECT_ID}/serviceAccounts`;
  const res = await authedFetch('POST', url, {
    accountId: opts.accountId,
    serviceAccount: {
      displayName: opts.displayName,
      description: opts.description,
    },
  });
  // Race-safe: if it was just created concurrently, fetch the existing one.
  if (res.status === 409) {
    const email = `${opts.accountId}@${GCP_PROJECT_ID}.iam.gserviceaccount.com`;
    const existing = await getServiceAccount(email);
    if (existing) return existing;
  }
  return parseOrThrow<GcpServiceAccount>(res, 'createServiceAccount');
}

/**
 * Idempotently ensures the workspace's SA exists. Returns its email.
 */
export async function ensureWorkspaceServiceAccount(opts: {
  slug: string;
  workspaceName?: string;
}): Promise<GcpServiceAccount> {
  const email = workspaceServiceAccountEmail(opts.slug);
  const existing = await getServiceAccount(email);
  if (existing) return existing;
  return createServiceAccount({
    accountId: workspaceServiceAccountId(opts.slug),
    displayName: `Vibn workspace: ${opts.workspaceName ?? opts.slug}`,
    description: `Auto-provisioned by Vibn for workspace "${opts.slug}". Owns workspace-scoped GCS bucket(s) and (eventually) Cloud Tasks queues + Scheduler jobs.`,
  });
}

// ────────────────────────────────────────────────────────────────────
// Service-account key minting
//
// We mint a JSON keyfile per workspace once at provision time and store
// it encrypted. Currently only used so app code can authenticate as the
// workspace's SA (e.g. to call GCS / Cloud Tasks from inside a deployed
// container). The control-plane itself uses the shared provisioner SA.
// ────────────────────────────────────────────────────────────────────

export interface GcpServiceAccountKey {
  /** Resource name, e.g. projects/.../serviceAccounts/.../keys/<id>. */
  name: string;
  /** Base64-encoded JSON keyfile (Google's privateKeyData format). */
  privateKeyData: string;
}

export async function createServiceAccountKey(saEmail: string): Promise<GcpServiceAccountKey> {
  const url = `${IAM_API}/projects/${GCP_PROJECT_ID}/serviceAccounts/${encodeURIComponent(
    saEmail,
  )}/keys`;
  const res = await authedFetch('POST', url, {
    privateKeyType: 'TYPE_GOOGLE_CREDENTIALS_FILE',
    keyAlgorithm: 'KEY_ALG_RSA_2048',
  });
  return parseOrThrow<GcpServiceAccountKey>(res, 'createServiceAccountKey');
}
