export function readConfig(env = process.env) {
  const rawRetentionDays = integer(env.GELIOS_RAW_RETENTION_DAYS, 14, 1, 3650);

  return {
    nodeEnv: string(env.NODE_ENV, "development"),
    port: integer(env.PORT, 18105, 1, 65535),
    databaseUrl: required(env.DATABASE_URL, "DATABASE_URL"),
    databasePoolSize: integer(env.GELIOS_DATABASE_POOL_SIZE, 10, 1, 50),
    internalAccessToken: optional(env.NODEDC_INTERNAL_ACCESS_TOKEN),
    tenantId: slug(required(env.GELIOS_TENANT_ID, "GELIOS_TENANT_ID"), "GELIOS_TENANT_ID"),
    connectionId: slug(required(env.GELIOS_CONNECTION_ID, "GELIOS_CONNECTION_ID"), "GELIOS_CONNECTION_ID"),
    unitScope: collectionScope(env.GELIOS_UNIT_SCOPE),
    allowedUnitIds: intList(env.GELIOS_ALLOWED_UNIT_IDS),
    intakeEnabled: boolean(env.GELIOS_INTAKE_ENABLED, false),
    rawRetentionDays,
    positionStaleAfterMs: integer(env.GELIOS_POSITION_STALE_AFTER_MS, 300_000, 1_000, 86_400_000),
  };
}

function collectionScope(value) {
  const candidate = optional(value).toLowerCase() || "allowlist";
  if (["allowlist", "all"].includes(candidate)) return candidate;
  throw new Error("GELIOS_UNIT_SCOPE_must_be_allowlist_or_all");
}

export function unitIsInScope(config, sourceUnitId) {
  return config?.unitScope === "all" || config?.allowedUnitIds?.includes(sourceUnitId) === true;
}

function required(value, name) {
  const normalized = optional(value);
  if (!normalized) throw new Error(`${name}_required`);
  return normalized;
}

function optional(value) {
  const normalized = String(value ?? "").trim();
  return normalized || "";
}

function string(value, fallback) {
  return optional(value) || fallback;
}

function integer(value, fallback, min, max) {
  const candidate = optional(value);
  if (!candidate) return fallback;
  const number = Number.parseInt(candidate, 10);
  if (!Number.isInteger(number) || number < min || number > max) {
    throw new Error(`invalid_integer:${candidate}`);
  }
  return number;
}

function boolean(value, fallback) {
  const candidate = optional(value).toLowerCase();
  if (!candidate) return fallback;
  if (["1", "true", "yes", "on"].includes(candidate)) return true;
  if (["0", "false", "no", "off"].includes(candidate)) return false;
  throw new Error(`invalid_boolean:${candidate}`);
}

function intList(value) {
  const values = optional(value)
    .split(",")
    .map((item) => item.trim())
    .filter(Boolean)
    .map((item) => Number.parseInt(item, 10));
  if (values.some((item) => !Number.isInteger(item) || item <= 0)) {
    throw new Error("GELIOS_ALLOWED_UNIT_IDS_must_contain_positive_integers");
  }
  return [...new Set(values)].sort((left, right) => left - right);
}

function slug(value, name) {
  if (!/^[a-z0-9][a-z0-9_-]{0,95}$/i.test(value)) {
    throw new Error(`${name}_invalid`);
  }
  return value;
}
