import { createHash } from "node:crypto";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";

import { DEV_DB_ENV_CONTRACT } from "../../internal/cloud/db-contract.ts";
import {
  PostgresCloudRuntimeStore,
  RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED,
  RUNTIME_DURABLE_ADAPTER_DRIVER_MISSING,
  RUNTIME_DURABLE_ADAPTER_MIGRATION_BLOCKED,
  RUNTIME_DURABLE_ADAPTER_QUERY_BLOCKED,
  RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED,
  RUNTIME_DURABLE_ADAPTER_SSL_BLOCKED,
  buildPostgresPoolConfig
} from "../../internal/db/runtime-store.ts";
import { ENVIRONMENT_DEV } from "../../internal/protocol/index.mjs";
import { ensureNotRepoReportsPath, tempReportPath } from "./report-paths.mjs";

const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const defaultReportPath = tempReportPath("dev-runtime-provisioning-report.json");
const issue = "pikasTech/HWLAB#311";

export const DEV_DB_ADMIN_SECRET_REF = Object.freeze({
  env: "HWLAB_CLOUD_DB_ADMIN_URL",
  secretName: "hwlab-cloud-api-dev-db-admin",
  secretKey: "admin-url"
});

const appSecretRef = DEV_DB_ENV_CONTRACT.secretRefs[0];
const systemDatabases = new Set(["postgres", "template0", "template1"]);

export async function runDevRuntimeProvisioningCli(argv = process.argv.slice(2), options = {}) {
  const args = parseArgs(argv);
  if (args.help) {
    process.stdout.write(`${usage()}\n`);
    return 0;
  }

  const report = await buildDevRuntimeProvisioningReport(args, options);
  if (args.writeReport) {
    await writeReport(report, args.reportPath, options.repoRoot ?? repoRoot);
  }

  const printable = args.pretty || args.writeReport ? report : summarizeReport(report);
  process.stdout.write(`${JSON.stringify(printable, null, 2)}\n`);

  if (report.safetyRefusal || (args.failOnBlocked && report.conclusion.status !== "ready")) {
    return 1;
  }
  return 0;
}

export async function buildDevRuntimeProvisioningReport(args = {}, options = {}) {
  const parsed = normalizeArgs(args);
  const env = options.env ?? process.env;
  const now = options.now ?? (() => new Date().toISOString());
  const target = parseProvisioningTarget(env.HWLAB_CLOUD_DB_URL);
  const admin = summarizeAdminEnv(env);
  const report = {
    issue,
    mode: parsed.mode,
    generatedAt: now(),
    target: {
      environment: ENVIRONMENT_DEV,
      namespace: DEV_DB_ENV_CONTRACT.dns.namespace,
      prodAllowed: false,
      dbUrlEnv: appSecretRef.env,
      dbUrlSecretRef: appSecretRef,
      adminUrlEnv: DEV_DB_ADMIN_SECRET_REF.env,
      adminUrlSecretRef: DEV_DB_ADMIN_SECRET_REF,
      dbEndpointAuthority: DEV_DB_ENV_CONTRACT.endpointAuthority.source
    },
    db: summarizeDbEnv(env, target, admin),
    applyBoundary: buildApplyBoundary(parsed),
    safety: buildSafety(parsed),
    actions: {
      liveDbReadAttempted: false,
      liveDbWriteAttempted: false,
      adminInspectionAttempted: false,
      adminWriteAttempted: false,
      appRuntimeReadinessAttempted: false,
      roleCreated: false,
      roleExisted: false,
      rolePasswordSynchronized: false,
      databaseCreated: false,
      databaseExisted: false,
      databaseConnectGranted: false,
      schemaPrivilegesGranted: false,
      readinessVerified: false
    },
    provisioning: {
      targetRole: emptyObjectState(target.roleNamePresent),
      targetDatabase: emptyObjectState(target.databaseNamePresent),
      privileges: {
        checked: false,
        ready: false,
        databaseConnectGranted: false,
        schemaPrivilegesGranted: false
      },
      ready: false
    },
    runtime: null,
    gates: sourceOnlyGates(target),
    downstreamRuntimeGates: null,
    blockers: [],
    safetyRefusal: false
  };

  validateSourceContract(report, target, parsed);

  if (parsed.mode === "check" || (parsed.mode === "dry-run" && !parsed.allowLiveDbRead)) {
    return finalizeReport(report);
  }

  if (parsed.mode === "dry-run") {
    if (!parsed.confirmDev) {
      addSafetyRefusal(report, "DEV DB provisioning live dry-run requires --confirm-dev.");
      return finalizeReport(report);
    }
    if (!target.ok) {
      return finalizeReport(report);
    }
    await verifyRuntimeReadiness(report, env, options);
    if (admin.present || options.adminClient) {
      await inspectAdminProvisioning(report, env, target, options);
    } else {
      inferProvisioningFromRuntime(report);
      if (!report.provisioning.ready) {
        addBlocker(report, "runtime_blocker", "runtime-provisioning-admin", "Role/database inspection requires the DEV admin SecretRef env when runtime readiness is not already green.", {
          adminUrlEnv: DEV_DB_ADMIN_SECRET_REF.env,
          adminUrlSecretRef: DEV_DB_ADMIN_SECRET_REF,
          valueRedacted: true,
          secretValuesPrinted: false
        });
      }
    }
    addRuntimeProvisioningBlocker(report);
    return finalizeReport(report);
  }

  if (parsed.mode === "apply") {
    if (!parsed.confirmDev || !parsed.confirmedNonProduction) {
      addSafetyRefusal(report, "DEV DB provisioning apply requires --confirm-dev and --confirmed-non-production.");
      return finalizeReport(report);
    }
    if (!target.ok) {
      return finalizeReport(report);
    }
    if (!admin.present && !options.adminClient) {
      addBlocker(report, "runtime_blocker", "runtime-provisioning-admin", "DEV DB provisioning apply requires the admin DB URL from its SecretRef env.", {
        adminUrlEnv: DEV_DB_ADMIN_SECRET_REF.env,
        adminUrlSecretRef: DEV_DB_ADMIN_SECRET_REF,
        valueRedacted: true,
        secretValuesPrinted: false
      });
      return finalizeReport(report);
    }
    await applyProvisioning(report, env, target, options);
    if (report.actions.adminWriteAttempted && report.blockers.length === 0) {
      await verifyRuntimeReadiness(report, env, options);
      inferProvisioningFromRuntime(report);
      addRuntimeProvisioningBlocker(report);
    }
    return finalizeReport(report);
  }

  addSafetyRefusal(report, `Unsupported DEV DB provisioning mode: ${parsed.mode}`);
  return finalizeReport(report);
}

export function parseArgs(argv = []) {
  const args = {
    mode: "check",
    allowLiveDbRead: false,
    confirmDev: false,
    confirmedNonProduction: false,
    writeReport: false,
    reportPath: defaultReportPath,
    pretty: false,
    failOnBlocked: false,
    help: false
  };

  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index];
    if (arg === "--check") args.mode = "check";
    else if (arg === "--dry-run") args.mode = "dry-run";
    else if (arg === "--apply") args.mode = "apply";
    else if (arg === "--allow-live-db-read") args.allowLiveDbRead = true;
    else if (arg === "--confirm-dev") args.confirmDev = true;
    else if (arg === "--confirmed-non-production") args.confirmedNonProduction = true;
    else if (arg === "--pretty") args.pretty = true;
    else if (arg === "--fail-on-blocked") args.failOnBlocked = true;
    else if (arg === "--help" || arg === "-h") args.help = true;
    else if (arg === "--report") {
      index += 1;
      args.reportPath = requireArgValue(argv[index], "--report");
      args.writeReport = true;
    } else if (arg.startsWith("--report=")) {
      args.reportPath = requireArgValue(arg.slice("--report=".length), "--report");
      args.writeReport = true;
    } else {
      throw new Error(`unknown argument: ${arg}`);
    }
  }

  return args;
}

export function parseProvisioningTarget(rawUrl) {
  const present = typeof rawUrl === "string" && rawUrl.trim().length > 0;
  if (!present) {
    return {
      ok: false,
      present: false,
      parseStatus: "missing",
      errorCode: "TARGET_DB_URL_MISSING",
      roleNamePresent: false,
      databaseNamePresent: false,
      passwordPresent: false
    };
  }

  let url;
  try {
    url = new URL(rawUrl);
  } catch {
    return {
      ok: false,
      present: true,
      parseStatus: "invalid_url",
      errorCode: "TARGET_DB_URL_INVALID",
      roleNamePresent: false,
      databaseNamePresent: false,
      passwordPresent: false
    };
  }

  const protocolReady = ["postgres:", "postgresql:"].includes(url.protocol);
  const roleName = decodeURIComponent(url.username || "");
  const password = decodeURIComponent(url.password || "");
  const databaseName = decodeURIComponent(url.pathname.replace(/^\/+/u, ""));
  const databaseAllowed = Boolean(databaseName) && !systemDatabases.has(databaseName.toLowerCase());
  const ok = protocolReady && Boolean(roleName) && Boolean(password) && databaseAllowed;
  return {
    ok,
    present: true,
    parseStatus: ok ? "ready" : "blocked",
    errorCode: ok ? null : targetParseErrorCode({ protocolReady, roleName, password, databaseName, databaseAllowed }),
    roleNamePresent: Boolean(roleName),
    databaseNamePresent: Boolean(databaseName),
    databaseAllowed,
    passwordPresent: Boolean(password),
    endpointClass: DEV_DB_ENV_CONTRACT.endpointAuthority.source,
    roleName,
    databaseName,
    password
  };
}

function normalizeArgs(args) {
  return {
    ...parseArgs([]),
    ...args
  };
}

function targetParseErrorCode({ protocolReady, roleName, password, databaseName, databaseAllowed }) {
  if (!protocolReady) return "TARGET_DB_URL_PROTOCOL_BLOCKED";
  if (!roleName) return "TARGET_DB_URL_ROLE_MISSING";
  if (!password) return "TARGET_DB_URL_PASSWORD_MISSING";
  if (!databaseName) return "TARGET_DB_URL_DATABASE_MISSING";
  if (!databaseAllowed) return "TARGET_DB_URL_DATABASE_FORBIDDEN";
  return "TARGET_DB_URL_BLOCKED";
}

function summarizeAdminEnv(env) {
  return {
    present: typeof env?.[DEV_DB_ADMIN_SECRET_REF.env] === "string" && env[DEV_DB_ADMIN_SECRET_REF.env].trim().length > 0,
    env: DEV_DB_ADMIN_SECRET_REF.env,
    secretRef: DEV_DB_ADMIN_SECRET_REF,
    valueRedacted: true
  };
}

function summarizeDbEnv(env, target, admin) {
  return {
    targetUrlPresent: target.present,
    targetUrlValueRedacted: true,
    targetEndpointRedacted: true,
    targetEndpointClass: target.endpointClass ?? DEV_DB_ENV_CONTRACT.endpointAuthority.source,
    targetParseStatus: target.parseStatus,
    targetErrorCode: target.errorCode,
    targetRoleNamePresent: target.roleNamePresent,
    targetDatabaseNamePresent: target.databaseNamePresent,
    targetDatabaseAllowed: target.databaseAllowed === true,
    targetPasswordPresent: target.passwordPresent,
    sslMode: env?.HWLAB_CLOUD_DB_SSL_MODE || DEV_DB_ENV_CONTRACT.nonSecretDefaults.HWLAB_CLOUD_DB_SSL_MODE,
    sslModeSecret: false,
    adminUrlPresent: admin.present,
    adminUrlValueRedacted: true,
    adminEndpointRedacted: true,
    dbUrlSecretRef: appSecretRef,
    adminUrlSecretRef: DEV_DB_ADMIN_SECRET_REF,
    endpointAuthority: DEV_DB_ENV_CONTRACT.endpointAuthority.source
  };
}

function validateSourceContract(report, target, args) {
  if (target.ok) return;
  if (args.mode === "check" && target.present === false) return;
  addBlocker(report, "runtime_blocker", "runtime-provisioning-target", "DEV DB provisioning requires a valid target DB URL from the cloud-api SecretRef env.", {
    env: appSecretRef.env,
    secretRef: appSecretRef,
    errorCode: target.errorCode,
    valueRedacted: true,
    endpointRedacted: true,
    secretValuesPrinted: false
  });
}

async function verifyRuntimeReadiness(report, env, options) {
  report.actions.liveDbReadAttempted = true;
  report.actions.appRuntimeReadinessAttempted = true;
  const runtimeStore = new PostgresCloudRuntimeStore({
    env,
    dbUrl: env?.HWLAB_CLOUD_DB_URL,
    sslMode: env?.HWLAB_CLOUD_DB_SSL_MODE,
    queryClient: options.queryClient,
    pgModuleLoader: options.pgModuleLoader
  });
  let runtime;
  try {
    runtime = await runtimeStore.readiness();
  } finally {
    if (!options.queryClient && typeof runtimeStore.pool?.end === "function") {
      await runtimeStore.pool.end();
    }
  }
  report.runtime = summarizeRuntime(runtime);
  report.actions.readinessVerified = runtime.ready === true;
  report.downstreamRuntimeGates = report.runtime.gates;
  report.gates = {
    ...report.gates,
    ssl: normalizeGate(runtime.gates?.ssl),
    auth: normalizeGate(runtime.gates?.auth),
    schema: normalizeGate(runtime.gates?.schema),
    migration: normalizeGate(runtime.gates?.migration),
    durability: normalizeGate(runtime.gates?.durability)
  };
}

async function inspectAdminProvisioning(report, env, target, options) {
  report.actions.liveDbReadAttempted = true;
  report.actions.adminInspectionAttempted = true;
  const { client, close } = await openAdminClient(env, options);
  try {
    const [role, database] = await Promise.all([
      objectExists(client, "SELECT 1 AS present FROM pg_catalog.pg_roles WHERE rolname = $1 LIMIT 1", [target.roleName]),
      objectExists(client, "SELECT 1 AS present FROM pg_catalog.pg_database WHERE datname = $1 LIMIT 1", [target.databaseName])
    ]);
    report.provisioning.targetRole = {
      checked: true,
      exists: role,
      created: false,
      ready: role,
      targetNamePresent: target.roleNamePresent
    };
    report.provisioning.targetDatabase = {
      checked: true,
      exists: database,
      created: false,
      ready: database,
      targetNamePresent: target.databaseNamePresent
    };
    report.actions.roleExisted = role;
    report.actions.databaseExisted = database;
    report.provisioning.ready = role && database && runtimeAuthSatisfied(report.runtime);
    if (!role) addMissingObjectBlocker(report, "role");
    if (!database) addMissingObjectBlocker(report, "database");
  } catch (error) {
    addClassifiedAdminBlocker(report, error, "DEV DB provisioning inspection could not verify the target role/database.");
  } finally {
    await close();
  }
}

async function applyProvisioning(report, env, target, options) {
  report.actions.liveDbWriteAttempted = true;
  report.actions.adminWriteAttempted = true;
  let admin;
  try {
    admin = await openAdminClient(env, options);
    const client = admin.client;
    const roleExists = await objectExists(client, "SELECT 1 AS present FROM pg_catalog.pg_roles WHERE rolname = $1 LIMIT 1", [target.roleName]);
    const databaseExists = await objectExists(client, "SELECT 1 AS present FROM pg_catalog.pg_database WHERE datname = $1 LIMIT 1", [target.databaseName]);
    report.actions.roleExisted = roleExists;
    report.actions.databaseExisted = databaseExists;

    if (!roleExists) {
      await client.query(`CREATE ROLE ${quotePgIdent(target.roleName)} LOGIN PASSWORD ${quotePgLiteral(target.password)}`);
      report.actions.roleCreated = true;
    } else {
      await client.query(`ALTER ROLE ${quotePgIdent(target.roleName)} WITH LOGIN PASSWORD ${quotePgLiteral(target.password)}`);
    }
    report.actions.rolePasswordSynchronized = true;

    if (!databaseExists) {
      await client.query(`CREATE DATABASE ${quotePgIdent(target.databaseName)} OWNER ${quotePgIdent(target.roleName)}`);
      report.actions.databaseCreated = true;
    }
    await client.query(`GRANT CONNECT ON DATABASE ${quotePgIdent(target.databaseName)} TO ${quotePgIdent(target.roleName)}`);
    report.actions.databaseConnectGranted = true;
  } catch (error) {
    addClassifiedAdminBlocker(report, error, "DEV DB provisioning apply could not complete role/database provisioning.");
    return;
  } finally {
    if (admin) await admin.close();
  }

  let targetAdmin;
  try {
    targetAdmin = await openAdminClient(env, {
      ...options,
      adminDbUrlOverride: withDatabaseInPostgresUrl(env[DEV_DB_ADMIN_SECRET_REF.env], target.databaseName)
    });
    await targetAdmin.client.query(`GRANT USAGE, CREATE ON SCHEMA public TO ${quotePgIdent(target.roleName)}`);
    report.actions.schemaPrivilegesGranted = true;
  } catch (error) {
    addClassifiedAdminBlocker(report, error, "DEV DB provisioning apply could not grant target database schema privileges.");
    return;
  } finally {
    if (targetAdmin) await targetAdmin.close();
  }

  report.provisioning.targetRole = {
    checked: true,
    exists: true,
    created: report.actions.roleCreated,
    ready: true,
    targetNamePresent: true
  };
  report.provisioning.targetDatabase = {
    checked: true,
    exists: true,
    created: report.actions.databaseCreated,
    ready: true,
    targetNamePresent: true
  };
  report.provisioning.privileges = {
    checked: true,
    ready: report.actions.databaseConnectGranted && report.actions.schemaPrivilegesGranted,
    databaseConnectGranted: report.actions.databaseConnectGranted,
    schemaPrivilegesGranted: report.actions.schemaPrivilegesGranted
  };
  report.provisioning.ready = report.provisioning.privileges.ready;
}

async function openAdminClient(env, options) {
  if (options.adminClient) {
    return {
      client: options.adminClient,
      close: async () => {}
    };
  }

  const pool = await createPgPool({
    dbUrl: options.adminDbUrlOverride ?? env[DEV_DB_ADMIN_SECRET_REF.env],
    sslMode: env.HWLAB_CLOUD_DB_SSL_MODE,
    timeoutMs: env.HWLAB_CLOUD_DB_PROBE_TIMEOUT_MS,
    pgModuleLoader: options.pgModuleLoader
  });
  return {
    client: pool,
    close: async () => {
      await pool.end();
    }
  };
}

async function createPgPool({ dbUrl, sslMode, timeoutMs, pgModuleLoader }) {
  let pg;
  try {
    pg = await (pgModuleLoader ? pgModuleLoader() : import("pg"));
  } catch (error) {
    if (error?.code === "ERR_MODULE_NOT_FOUND") {
      const driverError = new Error("Postgres provisioning requires the pg package");
      driverError.code = "HWLAB_PG_DRIVER_MISSING";
      throw driverError;
    }
    throw error;
  }
  const Pool = pg.Pool ?? pg.default?.Pool;
  if (typeof Pool !== "function") {
    const driverError = new Error("Postgres provisioning could not load pg.Pool");
    driverError.code = "HWLAB_PG_DRIVER_MISSING";
    throw driverError;
  }
  return new Pool(buildPostgresPoolConfig({
    dbUrl,
    sslMode,
    timeoutMs
  }));
}

function inferProvisioningFromRuntime(report) {
  const runtimeReady = report.runtime?.ready === true && report.runtime?.durable === true;
  const authReady = runtimeAuthSatisfied(report.runtime);
  if (!runtimeReady && !authReady) return;
  report.provisioning.targetRole = {
    ...report.provisioning.targetRole,
    checked: report.provisioning.targetRole.checked || false,
    exists: true,
    ready: true,
    inferredFromRuntime: true
  };
  report.provisioning.targetDatabase = {
    ...report.provisioning.targetDatabase,
    checked: report.provisioning.targetDatabase.checked || false,
    exists: true,
    ready: true,
    inferredFromRuntime: true
  };
  report.provisioning.ready = true;
}

function addRuntimeProvisioningBlocker(report) {
  const blocker = report.runtime?.blocker;
  if (!blocker) return;
  if (blocker === RUNTIME_DURABLE_ADAPTER_SSL_BLOCKED) {
    addBlocker(report, "runtime_blocker", "runtime-provisioning-ssl", "DEV DB provisioning reached the target runtime adapter, but SSL mode blocked connection.", runtimeEvidence(report.runtime));
  } else if (blocker === RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED) {
    addBlocker(report, "runtime_blocker", "runtime-provisioning-auth", "DEV DB target role/database exists check did not prove application authentication readiness.", runtimeEvidence(report.runtime));
  } else if (blocker === RUNTIME_DURABLE_ADAPTER_DRIVER_MISSING) {
    addBlocker(report, "runtime_blocker", "runtime-provisioning-driver", "Postgres driver is unavailable for DEV DB provisioning verification.", runtimeEvidence(report.runtime));
  }
}

function addMissingObjectBlocker(report, kind) {
  const scope = kind === "role" ? "runtime-provisioning-role" : "runtime-provisioning-database";
  addBlocker(report, "runtime_blocker", scope, `DEV DB target ${kind} is missing according to admin catalog inspection.`, {
    exists: false,
    created: false,
    valueRedacted: true,
    endpointRedacted: true,
    affectedRuntimeBlocker: RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED,
    secretValuesPrinted: false
  });
}

function addClassifiedAdminBlocker(report, error, summary) {
  const classified = classifyProvisioningError(error);
  addBlocker(report, "runtime_blocker", classified.scope, summary, {
    blocker: classified.blocker,
    errorCode: classified.errorCode,
    valueRedacted: true,
    endpointRedacted: true,
    secretValuesPrinted: false
  });
}

function classifyProvisioningError(error) {
  const errorCode = typeof error?.code === "string" ? error.code : "UNKNOWN";
  if (errorCode === "HWLAB_PG_DRIVER_MISSING") {
    return {
      blocker: RUNTIME_DURABLE_ADAPTER_DRIVER_MISSING,
      scope: "runtime-provisioning-driver",
      errorCode
    };
  }
  if (isSslError(error)) {
    return {
      blocker: RUNTIME_DURABLE_ADAPTER_SSL_BLOCKED,
      scope: "runtime-provisioning-ssl",
      errorCode
    };
  }
  if (["28P01", "28000", "42501"].includes(errorCode)) {
    return {
      blocker: RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED,
      scope: "runtime-provisioning-auth",
      errorCode
    };
  }
  if (["3D000"].includes(errorCode)) {
    return {
      blocker: RUNTIME_DURABLE_ADAPTER_AUTH_BLOCKED,
      scope: "runtime-provisioning-database",
      errorCode
    };
  }
  if (["42P01", "42703", "3F000"].includes(errorCode)) {
    return {
      blocker: RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED,
      scope: "runtime-provisioning-schema",
      errorCode
    };
  }
  return {
    blocker: RUNTIME_DURABLE_ADAPTER_QUERY_BLOCKED,
    scope: "runtime-provisioning-query",
    errorCode
  };
}

function runtimeEvidence(runtime) {
  return {
    blocker: runtime.blocker,
    queryResult: runtime.connection?.queryResult ?? "unknown",
    errorCode: runtime.connection?.errorCode ?? null,
    schemaReady: runtime.schema?.ready === true,
    migrationReady: runtime.migration?.ready === true,
    durableReady: runtime.ready === true && runtime.durable === true,
    secretValuesPrinted: false
  };
}

async function objectExists(client, sql, params) {
  const result = await client.query(sql, params);
  return Array.isArray(result?.rows) && result.rows.length > 0;
}

function sourceOnlyGates(target) {
  const sourceReady = target.ok;
  const targetMissingInSourceOnly = target.present === false;
  return {
    ssl: notCheckedGate("source-only validation does not connect to Postgres"),
    auth: sourceReady || targetMissingInSourceOnly ? notCheckedGate("auth requires live target runtime verification") : blockedGate("runtime_provisioning_target_blocked"),
    role: sourceReady || targetMissingInSourceOnly ? notCheckedGate("role existence requires DEV admin catalog inspection") : blockedGate("runtime_provisioning_target_blocked"),
    database: sourceReady || targetMissingInSourceOnly ? notCheckedGate("database existence requires DEV admin catalog inspection") : blockedGate("runtime_provisioning_target_blocked"),
    schema: notCheckedGate("schema is verified by runtime migration/readiness after role/database provisioning"),
    migration: notCheckedGate("migration ledger is verified by dev-runtime-migration"),
    durability: notCheckedGate("durability is verified by post-provisioning runtime readiness and postflight")
  };
}

function normalizeGate(gate = {}) {
  if (gate.ready === true || gate.status === "ready") return readyGate();
  if (gate.status === "blocked") return blockedGate(gate.blocker ?? "runtime_durable_adapter_blocked");
  return notCheckedGate();
}

function readyGate(summary = "ready") {
  return {
    checked: true,
    ready: true,
    status: "ready",
    blocker: null,
    summary
  };
}

function blockedGate(blocker, summary = "blocked") {
  return {
    checked: true,
    ready: false,
    status: "blocked",
    blocker,
    summary
  };
}

function notCheckedGate(summary = "not checked") {
  return {
    checked: false,
    ready: false,
    status: "not_checked",
    blocker: null,
    summary
  };
}

function emptyObjectState(targetNamePresent) {
  return {
    checked: false,
    exists: false,
    created: false,
    ready: false,
    targetNamePresent
  };
}

function summarizeRuntime(runtime = {}) {
  return {
    adapter: runtime.adapter ?? "unknown",
    durable: Boolean(runtime.durable),
    durableRequested: Boolean(runtime.durableRequested),
    ready: runtime.ready === true,
    status: runtime.status ?? "unknown",
    blocker: runtime.blocker ?? null,
    liveRuntimeEvidence: Boolean(runtime.liveRuntimeEvidence),
    fixtureEvidence: Boolean(runtime.fixtureEvidence),
    connection: {
      queryAttempted: Boolean(runtime.connection?.queryAttempted),
      queryResult: runtime.connection?.queryResult ?? "unknown",
      endpointRedacted: true,
      valueRedacted: true,
      errorCode: runtime.connection?.errorCode ?? null
    },
    schema: {
      checked: Boolean(runtime.schema?.checked),
      ready: Boolean(runtime.schema?.ready),
      missingTables: Array.isArray(runtime.schema?.missingTables) ? [...runtime.schema.missingTables] : [],
      missingColumns: Array.isArray(runtime.schema?.missingColumns) ? [...runtime.schema.missingColumns] : []
    },
    migration: {
      checked: Boolean(runtime.migration?.checked),
      ready: Boolean(runtime.migration?.ready),
      requiredMigrationId: runtime.migration?.requiredMigrationId ?? null,
      appliedMigrationId: runtime.migration?.appliedMigrationId ?? null,
      missing: runtime.migration?.missing !== false
    },
    gates: runtime.gates ?? {}
  };
}

function runtimeAuthSatisfied(runtime = {}) {
  if (runtime.ready === true && runtime.durable === true) return true;
  const blocker = runtime.blocker;
  return [
    RUNTIME_DURABLE_ADAPTER_SCHEMA_BLOCKED,
    RUNTIME_DURABLE_ADAPTER_MIGRATION_BLOCKED,
    RUNTIME_DURABLE_ADAPTER_QUERY_BLOCKED
  ].includes(blocker) && runtime.connection?.queryAttempted === true;
}

function buildApplyBoundary(args) {
  return {
    defaultMode: "check",
    mode: args.mode,
    liveDbReads: args.mode === "apply" || (args.mode === "dry-run" && args.allowLiveDbRead),
    liveDbWrites: args.mode === "apply",
    requiresForLiveDbRead: [
      "--confirm-dev",
      `${appSecretRef.env} from ${appSecretRef.secretName}/${appSecretRef.secretKey}`,
      `${DEV_DB_ADMIN_SECRET_REF.env} from ${DEV_DB_ADMIN_SECRET_REF.secretName}/${DEV_DB_ADMIN_SECRET_REF.secretKey} unless runtime readiness already proves auth`
    ],
    requiresForApply: [
      "--apply",
      "--confirm-dev",
      "--confirmed-non-production",
      `${appSecretRef.env} from ${appSecretRef.secretName}/${appSecretRef.secretKey}`,
      `${DEV_DB_ADMIN_SECRET_REF.env} from ${DEV_DB_ADMIN_SECRET_REF.secretName}/${DEV_DB_ADMIN_SECRET_REF.secretKey}`
    ],
    writeScope: args.mode === "apply"
      ? [
          "DEV Postgres target role named by the redacted cloud-api DB URL",
          "DEV Postgres target database named by the redacted cloud-api DB URL",
          "DEV target DB CONNECT and public schema CREATE/USAGE grants for that role"
        ]
      : [],
    noWriteScope: [
      "Kubernetes Secret resources",
      "PROD",
      "service restarts",
      "schema migrations",
      "M3/M4/M5 acceptance"
    ],
    forbidden: [
      "printing DB URLs, passwords, tokens, Secret values, or kubeconfig material",
      "manual psql or node stdin database writes",
      "mutating PROD"
    ]
  };
}

function buildSafety(args) {
  return {
    devOnly: true,
    environment: ENVIRONMENT_DEV,
    targetNamespace: DEV_DB_ENV_CONTRACT.dns.namespace,
    prodAllowed: false,
    sourceStoresSecretValues: false,
    printsSecretValues: false,
    secretValuesPrinted: false,
    dbUrlValueRedacted: true,
    adminUrlValueRedacted: true,
    endpointRedacted: true,
    readsKubernetesSecrets: false,
    writesKubernetesSecrets: false,
    liveDbWrites: args.mode === "apply",
    fixtureEvidenceAllowed: false
  };
}

function addBlocker(report, type, scope, summary, evidence = {}) {
  report.blockers.push({
    type,
    scope,
    status: "open",
    summary,
    sourceIssue: scope.includes("auth") ? "pikasTech/HWLAB#307" : issue,
    evidence
  });
}

function addSafetyRefusal(report, summary) {
  report.safetyRefusal = true;
  addBlocker(report, "safety_refusal", "runtime-provisioning-apply-boundary", summary, {
    devOnly: true,
    prodAllowed: false,
    secretValuesPrinted: false
  });
}

function finalizeReport(report) {
  const openBlockers = report.blockers.filter((blocker) => blocker.status === "open");
  report.provisioning.ready = report.provisioning.ready || (
    report.provisioning.targetRole.ready === true &&
    report.provisioning.targetDatabase.ready === true &&
    (report.provisioning.privileges.ready === true || report.mode !== "apply")
  );
  report.conclusion = {
    status: openBlockers.length === 0 ? "ready" : "blocked",
    summary: openBlockers.length === 0
      ? conclusionReadySummary(report)
      : "DEV DB provisioning is blocked; see separated role/database/ssl/auth blockers.",
    blockerCount: openBlockers.length
  };
  return report;
}

function conclusionReadySummary(report) {
  if (report.mode === "apply") {
    return "DEV DB role/database provisioning completed without printing secret values.";
  }
  if (report.mode === "dry-run" && report.actions.liveDbReadAttempted) {
    return "DEV DB role/database provisioning read-only verification passed without DB writes or secret output.";
  }
  return "DEV DB provisioning source contract is ready; no DB connection or live write was attempted.";
}

function summarizeReport(report) {
  return {
    issue: report.issue,
    mode: report.mode,
    conclusion: report.conclusion,
    db: report.db,
    actions: report.actions,
    provisioning: report.provisioning,
    gates: report.gates,
    downstreamRuntimeGates: report.downstreamRuntimeGates,
    blockers: report.blockers,
    safety: report.safety
  };
}

function quotePgIdent(value) {
  return `"${String(value).replaceAll('"', '""')}"`;
}

function quotePgLiteral(value) {
  return `'${String(value).replaceAll("'", "''")}'`;
}

function withDatabaseInPostgresUrl(rawUrl, databaseName) {
  const url = new URL(rawUrl);
  url.pathname = `/${encodeURIComponent(databaseName)}`;
  return url.toString();
}

function isSslError(error) {
  const code = typeof error?.code === "string" ? error.code : "";
  if (code.startsWith("ERR_SSL") || code.startsWith("ERR_TLS")) return true;
  const message = typeof error?.message === "string" ? error.message.toLowerCase() : "";
  return [
    "does not support ssl",
    "ssl is not enabled",
    "ssl negotiation",
    "ssl off",
    "ssl required",
    "requires ssl",
    "no ssl encryption",
    "no encryption",
    "hostssl",
    "server requires ssl",
    "tls handshake"
  ].some((pattern) => message.includes(pattern));
}

async function writeReport(report, reportPath, root) {
  const absolute = ensureNotRepoReportsPath(root, reportPath, "--report");
  await mkdir(path.dirname(absolute), { recursive: true });
  await writeFile(absolute, `${JSON.stringify(report, null, 2)}\n`);
}

function requireArgValue(value, flag) {
  if (typeof value !== "string" || value.trim() === "") {
    throw new Error(`${flag} requires a value`);
  }
  return value;
}

function usage() {
  return [
    "Usage: node scripts/dev-runtime-provisioning.mjs [--check|--dry-run|--apply]",
    "",
    "Default --check validates redacted DEV DB provisioning inputs only and never connects to DB.",
    "--dry-run is source-only unless --allow-live-db-read --confirm-dev is provided.",
    "--apply requires --confirm-dev --confirmed-non-production, HWLAB_CLOUD_DB_URL, and HWLAB_CLOUD_DB_ADMIN_URL.",
    "Reports never print DB URLs, passwords, tokens, Secret values, or kubeconfig material."
  ].join("\n");
}

export function formatDevRuntimeProvisioningFailure(error) {
  const message = redactFailureText(error instanceof Error ? error.message : String(error));
  return {
    issue,
    conclusion: {
      status: "blocked",
      summary: "DEV DB provisioning command failed before producing a report."
    },
    error: message,
    safety: {
      devOnly: true,
      prodAllowed: false,
      secretValuesPrinted: false
    },
    trace: createHash("sha256").update(message).digest("hex").slice(0, 12)
  };
}

function redactFailureText(value) {
  return String(value ?? "")
    .replace(/postgres(?:ql)?:\/\/[^\s"'<>]+/giu, "[redacted-postgres-url]")
    .replace(/(password\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]")
    .replace(/(token\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]")
    .replace(/(kubeconfig\s*[=:]\s*)[^\s,;]+/giu, "$1[redacted]");
}
