import {
  parseCurrentResourceKeys,
  shouldDropAsGlobalTableReplicationRecord
} from "./chunk-AWAWCRWW.mjs";
import {
  dynamodbImageToPlain
} from "./chunk-RC7HHZR6.mjs";
import {
  buildResourceSoftDeleteSql,
  buildResourceUpsertSql,
  ensureSchemaBootstrap
} from "./chunk-HQ4CPUPH.mjs";
import {
  decompressResource
} from "./chunk-APVVG7BO.mjs";
import "./chunk-KMEWULMX.mjs";
import "./chunk-LZOMFHX3.mjs";

// src/components/postgres/data-store-postgres-replication.handler.ts
import {
  GetSecretValueCommand,
  SecretsManagerClient
} from "@aws-sdk/client-secrets-manager";
import { Pool } from "pg";
var cachedConfig;
var pool;
var bootstrapPromise;
var secretsClient;
function readEnv(name) {
  const v = process.env[name]?.trim();
  if (!v) {
    throw new Error(
      `Missing required environment variable for postgres replication: ${name}`
    );
  }
  return v;
}
async function loadConfig() {
  if (cachedConfig) {
    return cachedConfig;
  }
  const host = readEnv("OPENHI_PG_HOST");
  const port = Number.parseInt(readEnv("OPENHI_PG_PORT"), 10);
  const database = readEnv("OPENHI_PG_DATABASE");
  const schema = readEnv("OPENHI_PG_SCHEMA");
  const directUser = process.env.OPENHI_PG_USER?.trim();
  const directPassword = process.env.OPENHI_PG_PASSWORD;
  if (directUser && directPassword !== void 0) {
    cachedConfig = {
      host,
      port,
      database,
      schema,
      user: directUser,
      password: directPassword,
      ssl: process.env.OPENHI_PG_SSL?.trim() === "true"
    };
    return cachedConfig;
  }
  const secretArn = process.env.OPENHI_PG_SECRET_ARN?.trim();
  if (!secretArn) {
    throw new Error(
      "Either OPENHI_PG_SECRET_ARN or both OPENHI_PG_USER and OPENHI_PG_PASSWORD must be set"
    );
  }
  if (!secretsClient) {
    secretsClient = new SecretsManagerClient({});
  }
  const out = await secretsClient.send(
    new GetSecretValueCommand({ SecretId: secretArn })
  );
  if (!out.SecretString) {
    throw new Error(
      `Secret ${secretArn} returned no SecretString; binary secrets are not supported`
    );
  }
  const parsed = JSON.parse(out.SecretString);
  cachedConfig = {
    host,
    port,
    database,
    schema,
    user: parsed.username,
    password: parsed.password,
    ssl: true
  };
  return cachedConfig;
}
async function getPool() {
  if (pool) {
    return pool;
  }
  const cfg = await loadConfig();
  pool = new Pool({
    host: cfg.host,
    port: cfg.port,
    database: cfg.database,
    user: cfg.user,
    password: cfg.password,
    ssl: cfg.ssl ? { rejectUnauthorized: false } : false,
    max: 2,
    idleTimeoutMillis: 6e4,
    connectionTimeoutMillis: 1e4
  });
  return pool;
}
async function ensureBootstrapped() {
  if (!bootstrapPromise) {
    bootstrapPromise = (async () => {
      const cfg = await loadConfig();
      const p = await getPool();
      await ensureSchemaBootstrap(p, cfg.schema);
    })().catch((err) => {
      bootstrapPromise = void 0;
      throw err;
    });
  }
  return bootstrapPromise;
}
function __resetReplicationModuleStateForTests() {
  cachedConfig = void 0;
  pool = void 0;
  bootstrapPromise = void 0;
  secretsClient = void 0;
}
function decodeKinesisRecord(record) {
  const json = Buffer.from(record.kinesis.data, "base64").toString("utf8");
  return JSON.parse(json);
}
function deriveLastUpdated(resource, approximateCreationEpochMs) {
  if (resource && typeof resource === "object") {
    const meta = resource.meta;
    const lu = meta?.lastUpdated;
    if (typeof lu === "string") {
      const parsed = new Date(lu);
      if (!Number.isNaN(parsed.getTime())) {
        return parsed;
      }
    }
  }
  if (typeof approximateCreationEpochMs === "number" && Number.isFinite(approximateCreationEpochMs)) {
    return new Date(approximateCreationEpochMs);
  }
  return /* @__PURE__ */ new Date();
}
function buildWriteIntent(change, awsRegion) {
  if (shouldDropAsGlobalTableReplicationRecord(change, awsRegion)) {
    return { kind: "drop" };
  }
  const parsed = parseCurrentResourceKeys(change);
  if (!parsed) {
    return { kind: "drop" };
  }
  const keys = {
    ...parsed,
    tenantId: parsed.tenantId.toLowerCase(),
    workspaceId: parsed.workspaceId.toLowerCase(),
    resourceId: parsed.resourceId.toLowerCase()
  };
  const isRemove = change.eventName === "REMOVE";
  const image = isRemove ? change.dynamodb?.OldImage : change.dynamodb?.NewImage;
  if (!image) {
    return { kind: "drop" };
  }
  const plain = dynamodbImageToPlain(image);
  const approxEpochMs = change.dynamodb?.ApproximateCreationDateTime;
  if (isRemove) {
    return {
      kind: "delete",
      params: {
        tenantId: keys.tenantId,
        workspaceId: keys.workspaceId,
        resourceType: keys.resourceType,
        resourceId: keys.resourceId,
        version: keys.version,
        deletedAt: deriveLastUpdated(void 0, approxEpochMs)
      }
    };
  }
  if (typeof plain.resource !== "string") {
    return { kind: "drop" };
  }
  let resourceJson;
  let resourceObj;
  try {
    resourceJson = decompressResource(plain.resource);
    resourceObj = JSON.parse(resourceJson);
  } catch {
    return { kind: "drop" };
  }
  return {
    kind: "upsert",
    params: {
      tenantId: keys.tenantId,
      workspaceId: keys.workspaceId,
      resourceType: keys.resourceType,
      resourceId: keys.resourceId,
      version: keys.version,
      lastUpdated: deriveLastUpdated(resourceObj, approxEpochMs),
      resourceJson
    }
  };
}
async function applyWriteIntent(client, schemaName, intent) {
  if (intent.kind === "drop") {
    return;
  }
  if (intent.kind === "upsert") {
    const p2 = intent.params;
    await Promise.resolve(
      client.query(buildResourceUpsertSql(schemaName), [
        p2.tenantId,
        p2.workspaceId,
        p2.resourceType,
        p2.resourceId,
        p2.version,
        p2.lastUpdated,
        p2.resourceJson
      ])
    );
    return;
  }
  const p = intent.params;
  await Promise.resolve(
    client.query(buildResourceSoftDeleteSql(schemaName), [
      p.tenantId,
      p.workspaceId,
      p.resourceType,
      p.resourceId,
      p.version,
      p.deletedAt
    ])
  );
}
async function handler(event) {
  const awsRegion = process.env.AWS_REGION ?? "";
  const batchItemFailures = [];
  await ensureBootstrapped();
  const cfg = await loadConfig();
  const p = await getPool();
  for (const record of event.Records) {
    try {
      const change = decodeKinesisRecord(record);
      const intent = buildWriteIntent(change, awsRegion);
      await applyWriteIntent(p, cfg.schema, intent);
    } catch (err) {
      console.error(
        "postgres replication record failed",
        record.kinesis.sequenceNumber,
        err
      );
      batchItemFailures.push({
        itemIdentifier: record.kinesis.sequenceNumber
      });
    }
  }
  return { batchItemFailures };
}
export {
  __resetReplicationModuleStateForTests,
  applyWriteIntent,
  buildWriteIntent,
  handler
};
//# sourceMappingURL=data-store-postgres-replication.handler.mjs.map