import { Firestore } from "@google-cloud/firestore";
import { env } from "../env";

/**
 * Read-only Firestore handle for the booking event-tracking data.
 *
 * The events are written by a system outside this monorepo; core only ever
 * reads them. Nothing in this module (or anything built on it) may write.
 *
 * Credentials default to the service account already configured for GCS — see
 * `lib/storage/gcs.ts`. The event data usually lives in a *different* (Firebase)
 * project from the GCS bucket, so both the project and the credentials can be
 * pointed elsewhere without touching the GCS setup:
 *
 *   EVENT_TRACKING_PROJECT_ID                    falls back to GOOGLE_CLOUD_PROJECT_ID
 *   EVENT_TRACKING_SERVICE_ACCOUNT_CREDENTIALS   falls back to GOOGLE_SERVICE_ACCOUNT_CREDENTIALS
 *   EVENT_TRACKING_DATABASE_ID                   defaults to "event-tracking"
 */

/**
 * The named database holding the events.
 *
 * This is NOT the project's `(default)` database. Omitting `databaseId` is the
 * single most likely way to break this module: the client silently connects to
 * `(default)`, every query succeeds, and every result is empty. If the module
 * returns zeroes across the board, check this first.
 */
export const DEFAULT_EVENT_TRACKING_DATABASE_ID = "event-tracking";

export function getEventTrackingDatabaseId(): string {
  return (
    process.env.EVENT_TRACKING_DATABASE_ID || DEFAULT_EVENT_TRACKING_DATABASE_ID
  );
}

export function getEventTrackingProjectId(): string {
  return (
    process.env.EVENT_TRACKING_PROJECT_ID ||
    env.GetString("GOOGLE_CLOUD_PROJECT_ID")
  );
}

let client: Firestore | null = null;

/**
 * Lazily created singleton. Built on first use rather than at import time so a
 * missing credential env var fails the request that needs it, instead of
 * preventing the whole core process from booting.
 */
export function getEventTrackingFirestore(): Firestore {
  if (client) return client;

  const credentialsJson =
    process.env.EVENT_TRACKING_SERVICE_ACCOUNT_CREDENTIALS ||
    env.GetString("GOOGLE_SERVICE_ACCOUNT_CREDENTIALS");

  client = new Firestore({
    projectId: getEventTrackingProjectId(),
    credentials: JSON.parse(credentialsJson) as Record<string, string>,
    databaseId: getEventTrackingDatabaseId(),
  });

  return client;
}

/** gRPC status codes worth naming, from google.rpc.Code. */
const GRPC_NOT_FOUND = 5;
const GRPC_PERMISSION_DENIED = 7;
const GRPC_UNAUTHENTICATED = 16;

/**
 * Turn a raw gRPC failure into something that says what to change.
 *
 * The Firestore client reports all three of "no such database", "no IAM" and
 * "bad key" as terse gRPC statuses — `5 NOT_FOUND:` with an empty detail string
 * is the worst of them, since it reads like a missing document rather than a
 * missing database. Each one has a different fix, so each gets its own message.
 */
export function describeFirestoreError(err: unknown): string | null {
  const code = (err as { code?: unknown } | null)?.code;
  const project = getEventTrackingProjectId();
  const database = getEventTrackingDatabaseId();

  switch (code) {
    case GRPC_NOT_FOUND:
      return `Firestore database "${database}" does not exist in project "${project}". The event data is usually in a separate Firebase project — set EVENT_TRACKING_PROJECT_ID (and EVENT_TRACKING_SERVICE_ACCOUNT_CREDENTIALS if that project needs its own service account).`;
    case GRPC_PERMISSION_DENIED:
      return `The service account cannot read Firestore database "${database}" in project "${project}". Grant it roles/datastore.viewer there.`;
    case GRPC_UNAUTHENTICATED:
      return `Firestore rejected the service account credentials for project "${project}". Check EVENT_TRACKING_SERVICE_ACCOUNT_CREDENTIALS / GOOGLE_SERVICE_ACCOUNT_CREDENTIALS.`;
    default:
      return null;
  }
}
