type VaultConfig = {
  address: string;
  token: string;
  mount: string;
  sharedPath: string;
  projectPath: string;
};

function requireEnv(name: string) {
  const value = process.env[name];
  if (!value) {
    throw new Error(`${name} is required when VAULT_ENABLED=true.`);
  }
  return value;
}

function vaultConfig(): VaultConfig {
  return {
    address: requireEnv('VAULT_ADDR').replace(/\/$/, ''),
    token: requireEnv('VAULT_TOKEN'),
    mount: requireEnv('VAULT_KV_MOUNT'),
    sharedPath: requireEnv('VAULT_SHARED_PATH'),
    projectPath: requireEnv('VAULT_PROJECT_PATH'),
  };
}

async function readVaultPath(config: VaultConfig, path: string) {
  const response = await fetch(
    `${config.address}/v1/${config.mount}/data/${path}`,
    { headers: { 'X-Vault-Token': config.token } },
  );

  if (!response.ok) {
    throw new Error(`Vault read failed for ${path}: ${response.status}.`);
  }

  const payload = (await response.json()) as {
    data?: { data?: Record<string, unknown> };
  };
  const data = payload.data?.data;
  if (!data) {
    throw new Error(`Vault path ${path} has no KV v2 data.`);
  }

  return data;
}

function applyEnvironment(values: Record<string, unknown>) {
  for (const [key, value] of Object.entries(values)) {
    if (typeof value !== 'string') {
      throw new Error(`Vault value ${key} must be a string.`);
    }
    process.env[key] = value;
  }
}

export async function loadVaultEnvironment() {
  if (process.env.VAULT_ENABLED !== 'true') {
    return;
  }

  const config = vaultConfig();
  applyEnvironment(await readVaultPath(config, config.sharedPath));
  applyEnvironment(await readVaultPath(config, config.projectPath));
}
