import type { RuntimeLogger } from "../../../core/types.ts";
import type { ISecretCodec } from "../../secrets/secret-codec-core.ts";
import type { DialectKind } from "./dialect/dialect-adapter.ts";

import { join } from "node:path";
import { createMysqlDialect } from "./dialect/mysql-dialect.ts";
import { createPostgresDialect } from "./dialect/postgres-dialect.ts";
import { createSqliteDialect } from "./dialect/sqlite-dialect.ts";
import { DrizzleRuntimeDatabase } from "./drizzle-runtime-database.ts";

export interface CreateRuntimeDatabaseOptions {
  dataDir: string;
  runLimit?: number;
  secretCodec?: ISecretCodec;
  logger?: RuntimeLogger;
  /** Override env for tests. */
  driver?: DialectKind;
  databaseUrl?: string;
  poolMax?: number;
}

/**
 * Build a Drizzle-backed RuntimeDatabase from OOMOL_CONNECT_DB_DRIVER
 * (sqlite | postgres | mysql, default sqlite) and related env vars.
 */
export async function createRuntimeDatabase(options: CreateRuntimeDatabaseOptions): Promise<DrizzleRuntimeDatabase> {
  const driver = options.driver ?? readDriver(process.env.OOMOL_CONNECT_DB_DRIVER);
  const poolMax = options.poolMax ?? readPoolMax(process.env.OOMOL_CONNECT_DB_POOL_MAX);
  const databaseUrl = options.databaseUrl ?? process.env.OOMOL_CONNECT_DATABASE_URL;

  if (driver === "sqlite") {
    const dialect = await createSqliteDialect(join(options.dataDir, "connect.sqlite"), {
      logger: options.logger,
    });
    return new DrizzleRuntimeDatabase({
      dialect,
      logger: options.logger,
      runLimit: options.runLimit,
      secretCodec: options.secretCodec,
    });
  }

  if (!databaseUrl) {
    throw new Error(`OOMOL_CONNECT_DATABASE_URL is required when OOMOL_CONNECT_DB_DRIVER=${driver}.`);
  }

  if (driver === "postgres") {
    const dialect = await createPostgresDialect({
      connectionString: databaseUrl,
      poolMax,
      logger: options.logger,
    });
    return new DrizzleRuntimeDatabase({
      dialect,
      logger: options.logger,
      runLimit: options.runLimit,
      secretCodec: options.secretCodec,
    });
  }

  if (driver === "mysql") {
    const dialect = await createMysqlDialect({
      connectionString: databaseUrl,
      poolMax,
      logger: options.logger,
    });
    return new DrizzleRuntimeDatabase({
      dialect,
      logger: options.logger,
      runLimit: options.runLimit,
      secretCodec: options.secretCodec,
    });
  }

  throw new Error(`Unsupported OOMOL_CONNECT_DB_DRIVER: ${String(driver)}`);
}

function readDriver(value: string | undefined): DialectKind {
  if (value === undefined || value === "") {
    return "sqlite";
  }
  if (value === "sqlite" || value === "postgres" || value === "mysql") {
    return value;
  }
  throw new Error(`Invalid OOMOL_CONNECT_DB_DRIVER="${value}". Expected sqlite, postgres, or mysql.`);
}

function readPoolMax(value: string | undefined): number {
  if (value === undefined || value === "") {
    return 10;
  }
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed < 1) {
    throw new Error(`Invalid OOMOL_CONNECT_DB_POOL_MAX="${value}". Expected a positive integer.`);
  }
  return parsed;
}
