// wall-e/tools/env-service.js
// Inspired by OpenCode's Env service
// (https://github.com/anomalyco/opencode, packages/opencode/src/env/index.ts)
// Scoped environment variable management with sensitive-key protection.
'use strict';

/**
 * EnvService — scoped environment variable management.
 *
 * OpenCode wraps process.env in an Effect-based service with get/set/remove.
 * Wall-E adapts this as a plain class with scoping support:
 * - A root EnvService reads from process.env
 * - scope(overrides) creates a child that inherits parent values but can override
 * - Sensitive keys are excluded from getAll() and export operations
 */
class EnvService {
  /**
   * @param {Record<string, string>} [source] - Base env (defaults to process.env)
   * @param {EnvService} [parent] - Parent env for scope chaining
   * @param {Set<string>} [sensitiveKeys] - Keys to exclude from getAll/export
   */
  constructor(source, parent, sensitiveKeys) {
    this._source = source || process.env;
    this._parent = parent || null;
    this._overrides = {};
    this._sensitiveKeys = sensitiveKeys || new Set([
      'ANTHROPIC_API_KEY',
      'OPENAI_API_KEY',
      'GOOGLE_API_KEY',
      'PORTKEY_API_KEY',
      'VOYAGE_API_KEY',
      'SLACK_BOT_TOKEN',
      'TELEGRAM_BOT_TOKEN',
      'GITHUB_TOKEN',
      'NPM_TOKEN',
    ]);
  }

  /**
   * Get an environment variable value.
   * Checks overrides first, then source, then parent.
   * @param {string} key
   * @returns {string|undefined}
   */
  get(key) {
    if (key in this._overrides) return this._overrides[key];
    if (key in this._source) return this._source[key];
    if (this._parent) return this._parent.get(key);
    return undefined;
  }

  /**
   * Set an environment variable in the override layer.
   * Does NOT modify process.env — only affects this instance and its children.
   * @param {string} key
   * @param {string} value
   */
  set(key, value) {
    this._overrides[key] = value;
  }

  /**
   * Mark a key as sensitive (excluded from getAll/export).
   * @param {string} key
   */
  markSensitive(key) {
    this._sensitiveKeys.add(key);
  }

  /**
   * Check if a key is marked sensitive.
   * @param {string} key
   * @returns {boolean}
   */
  isSensitive(key) {
    return this._sensitiveKeys.has(key);
  }

  /**
   * Get all environment variables, excluding sensitive keys.
   * Merges parent -> source -> overrides (later wins).
   * @returns {Record<string, string>}
   */
  getAll() {
    const merged = {};

    // Start with parent values
    if (this._parent) {
      Object.assign(merged, this._parent.getAll());
    }

    // Layer in source values
    for (const [key, value] of Object.entries(this._source)) {
      if (value !== undefined && !this._sensitiveKeys.has(key)) {
        merged[key] = value;
      }
    }

    // Layer in overrides
    for (const [key, value] of Object.entries(this._overrides)) {
      if (!this._sensitiveKeys.has(key)) {
        merged[key] = value;
      }
    }

    return merged;
  }

  /**
   * Create a scoped child env that inherits this env but can override values.
   * Useful for giving a coding task its own env without polluting the parent.
   *
   * @param {Record<string, string>} overrides - Key-value overrides for the child scope
   * @returns {EnvService}
   */
  scope(overrides = {}) {
    const child = new EnvService({}, this, new Set(this._sensitiveKeys));
    for (const [key, value] of Object.entries(overrides)) {
      child.set(key, value);
    }
    return child;
  }

  /**
   * Convert to a plain object suitable for passing to child_process.spawn({ env }).
   * Includes sensitive keys (needed for actual process execution).
   * @returns {Record<string, string>}
   */
  toProcessEnv() {
    const merged = {};

    if (this._parent) {
      Object.assign(merged, this._parent.toProcessEnv());
    }

    for (const [key, value] of Object.entries(this._source)) {
      if (value !== undefined) merged[key] = value;
    }

    for (const [key, value] of Object.entries(this._overrides)) {
      merged[key] = value;
    }

    return merged;
  }
}

module.exports = { EnvService };
