const readyQueryResults = Object.freeze(["durable_readiness_ready", "runtime_durable_ready", "query_ready", "readiness_ready", "ready", "ok", "pass", "passed"]);
const blockedQueryResults = Object.freeze<Record<string, string>>({
  driver_missing: "runtime_durable_adapter_driver_missing",
  ssl_negotiation_blocked: "runtime_durable_adapter_ssl_blocked",
  auth_blocked: "runtime_durable_adapter_auth_blocked",
  schema_blocked: "runtime_durable_adapter_schema_blocked",
  migration_blocked: "runtime_durable_adapter_migration_blocked",
  query_blocked: "runtime_durable_adapter_query_blocked",
  not_ready: "runtime_durable_adapter_query_blocked"
});
const gateLayers = Object.freeze(["ssl", "auth", "schema", "migration", "durability"]);

export function classifyRuntimeDurableReadiness(payload: Record<string, unknown> = {}, options: { requirePostgresAdapter?: boolean } = {}) {
  const runtime = record(payload.runtime);
  const readiness = record(payload.readiness);
  const durability = record(readiness.durability);
  const db = record(payload.db);
  const dbRuntimeReadiness = record(db.runtimeReadiness);
  const gates = record(runtime.gates ?? durability.gates);
  const gateBlocker = firstBlockedRuntimeGate(gates);
  const queryResult = firstNonEmpty(record(runtime.connection).queryResult, durability.queryResult, dbRuntimeReadiness.queryResult);
  const queryBlocker = runtimeDurableBlockerFromQueryResult(queryResult);
  const readyQueryResult = isRuntimeDurableReadyQueryResult(queryResult);
  const explicitDurabilityReady = durabilityReadySignal({ runtime, durability, dbRuntimeReadiness, gateBlocker, queryResult, readyQueryResult, queryBlocker });
  const directBlocker = firstNonEmpty(
    explicitDurabilityReady ? "" : runtime.blocker,
    durability.blocker,
    dbRuntimeReadiness.blocker,
    explicitDurabilityReady ? "" : blockerCodeStartingWith(payload, "runtime_durable_adapter_")
  );
  const reasonCode = firstNonEmpty(directBlocker, gateBlocker?.blocker, queryBlocker);
  const blockedLayer = firstNonEmpty(
    record(runtime.durabilityContract).blockedLayer,
    durability.blockedLayer,
    dbRuntimeReadiness.blockedLayer,
    gateBlocker?.layer,
    runtimeDurableLayerFromBlocker(reasonCode),
    runtimeDurableLayerFromQueryResult(queryResult)
  );
  const adapter = firstNonEmpty(runtime.adapter, durability.adapter, dbRuntimeReadiness.adapter, "unknown");
  const runtimeDurable = runtime.durable === true || durability.durable === true || dbRuntimeReadiness.durable === true;
  const runtimeExplicitlyNonDurable = runtime.durable === false || durability.durable === false || dbRuntimeReadiness.durable === false;
  const runtimeReady = runtime.ready === true || durability.runtimeReady === true || dbRuntimeReadiness.ready === true;
  const durabilityReady = durability.ready === true || dbRuntimeReadiness.ready === true || runtimeReady;
  const liveRuntimeEvidence = runtime.liveRuntimeEvidence === true || durability.liveRuntimeEvidence === true || dbRuntimeReadiness.liveRuntimeEvidence === true;
  const rawStatus = rawStatusFrom(payload);
  const postgresRequired = options.requirePostgresAdapter === true;
  const ready = (!postgresRequired || adapter === "postgres") && runtimeDurable && runtimeReady && durabilityReady && liveRuntimeEvidence && !reasonCode && !blockedLayer && (!queryResult || readyQueryResult);

  if (ready) {
    return {
      status: "ready",
      ready: true,
      classification: "runtime_durable_ready",
      reasonCode: "runtime_durable_ready",
      reason: `runtime durable ready; adapter=${adapter}; queryResult=${queryResult || "not_observed"}`,
      impact: "HWPOD runtime readiness checks may rely on durable runtime evidence when persistence readiness is green.",
      safeNextAction: "Continue with repo-owned DEV CD postflight; do not read or print Secret values.",
      retryable: true,
      readonly: false,
      evidence: runtimeDurableEvidence({ adapter, runtimeDurable, runtimeReady, durabilityReady, liveRuntimeEvidence, blockedLayer: null, queryResult, readyQueryResult, reasonCode: null, gates, rawStatus, postgresRequired })
    };
  }

  const nonReadyReasonCode = firstNonEmpty(
    reasonCode,
    postgresRequired && adapter !== "postgres" ? "runtime_durable_adapter_not_postgres" : "",
    runtimeExplicitlyNonDurable ? "runtime_durable_false" : "",
    runtimeDurable ? "" : "runtime_durable_adapter_missing",
    runtimeReady ? "" : "runtime_durable_runtime_not_ready",
    liveRuntimeEvidence ? "" : "runtime_durable_live_evidence_missing",
    blockedRuntimeStatus(rawStatus) ? rawStatus : "",
    "runtime_durable_adapter_not_ready"
  );
  const layer = firstNonEmpty(blockedLayer, runtimeDurableLayerFromBlocker(nonReadyReasonCode), runtimeExplicitlyNonDurable ? "runtime-durable" : "", "runtime-durable");
  return {
    status: "blocked",
    ready: false,
    classification: nonReadyReasonCode,
    reasonCode: nonReadyReasonCode,
    reason: runtimeDurableBlockedReason({ reasonCode: nonReadyReasonCode, adapter, layer, queryResult }),
    impact: "HWPOD runtime readiness remains source-level while durable runtime evidence is blocked.",
    safeNextAction: runtimeDurableSafeNextAction(nonReadyReasonCode, layer),
    retryable: true,
    readonly: true,
    evidence: runtimeDurableEvidence({ adapter, runtimeDurable, runtimeReady, durabilityReady, liveRuntimeEvidence, blockedLayer: layer, queryResult, readyQueryResult, reasonCode: nonReadyReasonCode, gates, rawStatus, postgresRequired })
  };
}

function durabilityReadySignal(input: { runtime: Record<string, unknown>; durability: Record<string, unknown>; dbRuntimeReadiness: Record<string, unknown>; gateBlocker: { layer: string; blocker: string } | null; queryResult: string; readyQueryResult: boolean; queryBlocker: string }): boolean {
  if (input.gateBlocker || input.queryBlocker) return false;
  if (input.queryResult && !input.readyQueryResult) return false;
  const ready = input.durability.ready === true || input.dbRuntimeReadiness.ready === true || record(input.runtime.durabilityContract).ready === true;
  if (!ready) return false;
  return !firstNonEmpty(input.durability.blocker, input.dbRuntimeReadiness.blocker, record(input.runtime.durabilityContract).blocker, input.durability.blockedLayer, input.dbRuntimeReadiness.blockedLayer, record(input.runtime.durabilityContract).blockedLayer);
}

function firstBlockedRuntimeGate(gates: Record<string, unknown>): { layer: string; blocker: string } | null {
  for (const layer of gateLayers) {
    const gate = record(gates[layer]);
    const status = normalized(gate.status);
    if (gate.ready === false || status === "blocked" || status === "failed") return { layer, blocker: firstNonEmpty(gate.blocker, blockerForLayer(layer)) };
  }
  return null;
}

function blockerForLayer(layer: string): string {
  if (layer === "ssl") return "runtime_durable_adapter_ssl_blocked";
  if (layer === "auth") return "runtime_durable_adapter_auth_blocked";
  if (layer === "schema") return "runtime_durable_adapter_schema_blocked";
  if (layer === "migration") return "runtime_durable_adapter_migration_blocked";
  return "runtime_durable_adapter_query_blocked";
}

function runtimeDurableLayerFromBlocker(blocker: string): string {
  const text = normalized(blocker);
  if (text.includes("ssl")) return "ssl";
  if (text.includes("auth")) return "auth";
  if (text.includes("schema")) return "schema";
  if (text.includes("migration")) return "migration";
  if (text.includes("query")) return "durability_query";
  if (text.includes("missing") || text.includes("unconfigured") || text.includes("driver")) return "adapter";
  return "";
}

function runtimeDurableLayerFromQueryResult(queryResult: string): string {
  const text = normalized(queryResult);
  if (!text || isRuntimeDurableReadyQueryResult(text)) return "";
  if (text.includes("ssl")) return "ssl";
  if (text.includes("auth")) return "auth";
  if (text.includes("schema")) return "schema";
  if (text.includes("migration")) return "migration";
  if (text.includes("query")) return "durability_query";
  if (text.includes("driver")) return "adapter";
  return "";
}

function runtimeDurableBlockerFromQueryResult(queryResult: string): string {
  const text = normalized(queryResult);
  if (!text || isRuntimeDurableReadyQueryResult(text)) return "";
  return blockedQueryResults[text] ?? "";
}

function isRuntimeDurableReadyQueryResult(queryResult: string): boolean {
  return readyQueryResults.includes(normalized(queryResult));
}

function runtimeDurableEvidence(input: { adapter: string; runtimeDurable: boolean; runtimeReady: boolean; durabilityReady: boolean; liveRuntimeEvidence: boolean; blockedLayer: string | null; queryResult: string; readyQueryResult: boolean; reasonCode: string | null; gates: Record<string, unknown>; rawStatus: unknown; postgresRequired: boolean }) {
  return {
    adapter: input.adapter,
    durable: input.runtimeDurable,
    runtimeReady: input.runtimeReady,
    durabilityReady: input.durabilityReady,
    liveRuntimeEvidence: input.liveRuntimeEvidence,
    blocker: input.reasonCode,
    blockedLayer: input.blockedLayer,
    queryResult: input.queryResult || null,
    readyQueryResult: input.readyQueryResult,
    rawStatus: input.rawStatus || null,
    postgresRequired: input.postgresRequired,
    gates: Object.fromEntries(gateLayers.map((layer) => {
      const gate = record(input.gates[layer]);
      return [layer, { checked: gate.checked === true, ready: gate.ready === true, status: gate.status ?? "unknown", blocker: gate.blocker ?? null }];
    })),
    secretMaterialRead: false
  };
}

function runtimeDurableBlockedReason(input: { reasonCode: string; adapter: string; layer: string; queryResult: string }): string {
  if (input.reasonCode === "runtime_durable_false") return `runtime durable=false; adapter=${input.adapter}; queryResult=${input.queryResult || "not_observed"}`;
  if (input.reasonCode === "runtime_durable_adapter_not_postgres") return `runtime durable adapter is not postgres; adapter=${input.adapter}; queryResult=${input.queryResult || "not_observed"}`;
  return `runtime durable blocked at ${input.layer || "runtime-durable"}; blocker=${input.reasonCode}; queryResult=${input.queryResult || "unknown"}`;
}

function runtimeDurableSafeNextAction(reasonCode: string, layer: string): string {
  const text = normalized(`${reasonCode} ${layer}`);
  if (text.includes("auth")) return "Repair DEV DB auth/SecretRef metadata through repo-owned provisioning, then rerun postflight without printing Secret values.";
  if (text.includes("schema") || text.includes("migration")) return "Run repo-owned DEV runtime provisioning and migration, then rerun postflight.";
  if (text.includes("ssl")) return "Align DEV DB SSL mode with the repo-owned DEV contract, then rerun postflight.";
  if (text.includes("query")) return "Inspect durable runtime query readiness, preserve evidence persistence, and rerun postflight.";
  if (text.includes("postgres")) return "Run DEV with HWLAB_CLOUD_RUNTIME_ADAPTER=postgres and DB URLs injected only through SecretRefs.";
  return "Restore durable runtime readiness through repo-owned DEV CD steps, then rerun postflight.";
}

function rawStatusFrom(payload: Record<string, unknown>): unknown {
  return payload.status ?? record(payload.readiness).status ?? record(payload.runtime).status ?? record(record(payload.readiness).durability).status ?? "";
}

function blockerCodeStartingWith(payload: Record<string, unknown>, prefix: string): string {
  const readiness = record(payload.readiness);
  return [...array(payload.blockerCodes), ...array(readiness.blockerCodes)].map(String).find((code) => code.startsWith(prefix)) ?? "";
}

function blockedRuntimeStatus(value: unknown): boolean {
  return ["error", "failed", "failure", "blocked", "unavailable", "rejected", "degraded"].includes(normalized(value));
}

function record(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" ? value as Record<string, unknown> : {};
}

function array(value: unknown): unknown[] {
  return Array.isArray(value) ? value : [];
}

function firstNonEmpty(...values: unknown[]): string {
  for (const value of values) {
    const text = String(value ?? "").trim();
    if (text) return text;
  }
  return "";
}

function normalized(value: unknown): string {
  return String(value ?? "").trim().toLowerCase();
}
