/**
 * Credential config loader for WASM tool sandbox.
 *
 * Reads a YAML or JSON configuration file that maps tool names to arrays
 * of allowed secret key names. Safe default: if no config file exists,
 * returns an empty allowlist (no secrets accessible to any tool).
 *
 * Example config (YAML):
 * ```yaml
 * nerf_decode: []
 * search_web:
 *   - TAVILY_API_KEY
 *   - BRAVE_API_KEY
 * ```
 *
 * Example config (JSON):
 * ```json
 * {
 *   "nerf_decode": [],
 *   "search_web": ["TAVILY_API_KEY", "BRAVE_API_KEY"]
 * }
 * ```
 */

import { readFile } from 'node:fs/promises';
import { existsSync } from 'node:fs';
import { parse as parseYaml } from 'yaml';

/**
 * @typedef {Record<string, string[]>} CredentialAllowlist
 * Map of tool names to arrays of allowed secret key names.
 */

/**
 * Load and validate a credential allowlist configuration file.
 *
 * @param {string} configPath - Path to YAML or JSON config file
 * @returns {Promise<CredentialAllowlist>} Validated allowlist, or empty object if file missing
 * @throws {Error} If the file exists but has invalid structure
 */
export async function loadCredentialConfig(configPath) {
  if (!configPath || !existsSync(configPath)) {
    return {};
  }

  let raw;
  try {
    raw = await readFile(configPath, 'utf-8');
  } catch (err) {
    throw new Error(
      `credential-config: failed to read "${configPath}": ${err.message}`
    );
  }

  let parsed;
  try {
    // Try YAML first (superset of JSON)
    parsed = parseYaml(raw);
  } catch (yamlErr) {
    try {
      parsed = JSON.parse(raw);
    } catch {
      throw new Error(
        `credential-config: failed to parse "${configPath}" as YAML or JSON: ${yamlErr.message}`
      );
    }
  }

  return validateAllowlist(parsed, configPath);
}

/**
 * Validate that a parsed config has the correct structure.
 *
 * @param {unknown} config - Parsed configuration
 * @param {string} source - Source path for error messages
 * @returns {CredentialAllowlist} Validated allowlist
 * @throws {Error} If the structure is invalid
 */
export function validateAllowlist(config, source = '<input>') {
  if (config === null || config === undefined) {
    return {};
  }

  if (typeof config !== 'object' || Array.isArray(config)) {
    throw new Error(
      `credential-config: config from "${source}" must be an object mapping tool names to secret key arrays`
    );
  }

  /** @type {CredentialAllowlist} */
  const result = {};

  for (const [toolName, keys] of Object.entries(config)) {
    if (typeof toolName !== 'string') {
      throw new Error(
        `credential-config: tool name must be a string, got ${typeof toolName} in "${source}"`
      );
    }

    if (!Array.isArray(keys)) {
      throw new Error(
        `credential-config: allowlist for tool "${toolName}" must be an array of key names in "${source}"`
      );
    }

    for (let i = 0; i < keys.length; i++) {
      if (typeof keys[i] !== 'string') {
        throw new Error(
          `credential-config: key at index ${i} for tool "${toolName}" must be a string in "${source}"`
        );
      }
    }

    result[toolName] = [...keys];
  }

  return result;
}
