import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { mkdtempSync, rmSync, writeFileSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
import { loadConfig } from './config.js';
import { ConfigManager } from './config-manager.js';

// New coverage for the env-override matrix (inventory gaps CFG-004, CFG-016).
// Only OLLAMA_BASE_URL was previously asserted (config.audit-regression). Here we
// drive the REAL loadConfig() for CONCURRENCY / WORKTREE_DIR / OLLAMA_MODEL, and
// the REAL ConfigManager for DB_PATH (which only surfaces as an overriddenByEnv
// flag — it is not consumed by loadConfig).
//
// Env hygiene: snapshot the keys we touch in beforeEach, fully restore in
// afterEach so nothing leaks across tests or files.

const MANAGED_ENV = [
  'CONCURRENCY',
  'WORKTREE_DIR',
  'OLLAMA_MODEL',
  'OLLAMA_BASE_URL',
  'DB_PATH',
  'AAO_CONFIG',
] as const;

function withTempConfig(yaml: string, fn: (path: string) => void): void {
  const dir = mkdtempSync(join(tmpdir(), 'maestro-cfg-env-'));
  try {
    const p = join(dir, 'config.yaml');
    writeFileSync(p, yaml);
    fn(p);
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
}

describe('config env-override matrix', () => {
  const snapshot: Record<string, string | undefined> = {};

  beforeEach(() => {
    // Snapshot then clear so each test starts from a known no-env baseline.
    for (const k of MANAGED_ENV) {
      snapshot[k] = process.env[k];
      delete process.env[k];
    }
  });

  afterEach(() => {
    for (const k of MANAGED_ENV) {
      const v = snapshot[k];
      if (v === undefined) delete process.env[k];
      else process.env[k] = v;
    }
  });

  // --- CONCURRENCY -----------------------------------------------------------

  describe('CONCURRENCY', () => {
    it('overrides config.concurrency when set to a valid integer', () => {
      withTempConfig('config_version: 2\nconcurrency: 2\n', (p) => {
        process.env['CONCURRENCY'] = '7';
        expect(loadConfig(p).concurrency).toBe(7);
      });
    });

    it('falls back to the YAML value when unset', () => {
      withTempConfig('config_version: 2\nconcurrency: 4\n', (p) => {
        expect(loadConfig(p).concurrency).toBe(4);
      });
    });

    it('falls back to the default (1) when neither env nor YAML provides it', () => {
      withTempConfig('config_version: 2\n', (p) => {
        expect(loadConfig(p).concurrency).toBe(1);
      });
    });

    it('ignores a non-numeric CONCURRENCY and keeps the YAML value', () => {
      withTempConfig('config_version: 2\nconcurrency: 3\n', (p) => {
        process.env['CONCURRENCY'] = 'not-a-number';
        expect(loadConfig(p).concurrency).toBe(3);
      });
    });
  });

  // --- WORKTREE_DIR ----------------------------------------------------------

  describe('WORKTREE_DIR', () => {
    it('overrides both config.worktreeDir and config.storage.worktreeDir when set', () => {
      withTempConfig('config_version: 2\nstorage:\n  worktree_dir: /yaml/wt\n', (p) => {
        process.env['WORKTREE_DIR'] = '/env/override/wt';
        const config = loadConfig(p);
        expect(config.worktreeDir).toBe('/env/override/wt');
        expect(config.storage?.worktreeDir).toBe('/env/override/wt');
      });
    });

    it('falls back to the YAML storage value when unset', () => {
      withTempConfig('config_version: 2\nstorage:\n  worktree_dir: /yaml/wt\n', (p) => {
        expect(loadConfig(p).worktreeDir).toBe('/yaml/wt');
      });
    });

    it('falls back to the default worktreeDir when neither env nor YAML provides it', () => {
      withTempConfig('config_version: 2\n', (p) => {
        expect(loadConfig(p).worktreeDir).toBe('/var/lib/maestro/workspaces');
      });
    });
  });

  // --- OLLAMA_MODEL ----------------------------------------------------------

  describe('OLLAMA_MODEL', () => {
    it('overrides provider.model and the first provider worker model when set', () => {
      withTempConfig(
        'config_version: 2\nllm:\n  workers:\n    - name: w1\n      endpoint: http://h:11434/v1\n      model: yaml-model\n',
        (p) => {
          process.env['OLLAMA_MODEL'] = 'env-model';
          const config = loadConfig(p);
          expect(config.provider.model).toBe('env-model');
          // post-normalize override targets the executed provider.workers[0]
          expect(config.provider.workers[0]?.model).toBe('env-model');
        },
      );
    });

    it('does NOT bake the env override into the persisted llm block', () => {
      withTempConfig(
        'config_version: 2\nllm:\n  workers:\n    - name: w1\n      endpoint: http://h:11434/v1\n      model: yaml-model\n',
        (p) => {
          process.env['OLLAMA_MODEL'] = 'env-model';
          const config = loadConfig(p);
          // llm.workers (the block config-manager persists) must keep the YAML value.
          expect(config.llm.workers[0]?.model).toBe('yaml-model');
        },
      );
    });

    it('falls back to the default provider model (qwen3:32b) when unset', () => {
      withTempConfig('config_version: 2\n', (p) => {
        expect(loadConfig(p).provider.model).toBe('qwen3:32b');
      });
    });
  });

  // --- DB_PATH (surfaces only as an overriddenByEnv flag) --------------------

  describe('DB_PATH', () => {
    it('flags dbPath as env-overridden in ConfigManager.getConfigForApi when set', () => {
      withTempConfig('config_version: 2\n', (p) => {
        process.env['DB_PATH'] = '/env/override/maestro.db';
        const mgr = new ConfigManager(p);
        const { overriddenByEnv } = mgr.getConfigForApi();
        expect(overriddenByEnv['dbPath']).toBe(true);
      });
    });

    it('does NOT flag dbPath when DB_PATH is unset', () => {
      withTempConfig('config_version: 2\n', (p) => {
        const mgr = new ConfigManager(p);
        const { overriddenByEnv } = mgr.getConfigForApi();
        expect(overriddenByEnv['dbPath']).toBeUndefined();
      });
    });
  });

  // --- Cross-check: the documented flags appear for each env var -------------

  describe('overriddenByEnv flag matrix (config-manager)', () => {
    it('flags concurrency, storage.worktreeDir, and llm.workers[0].model together', () => {
      withTempConfig('config_version: 2\n', (p) => {
        process.env['CONCURRENCY'] = '3';
        process.env['WORKTREE_DIR'] = '/env/wt';
        process.env['OLLAMA_MODEL'] = 'env-model';
        const mgr = new ConfigManager(p);
        const { overriddenByEnv } = mgr.getConfigForApi();
        expect(overriddenByEnv['concurrency']).toBe(true);
        expect(overriddenByEnv['storage.worktreeDir']).toBe(true);
        expect(overriddenByEnv['llm.workers[0].model']).toBe(true);
      });
    });
  });
});
