import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { resolveConfigPath } from '../src/config.js';

function makeTempDir(prefix: string): string {
  return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
}

describe('resolveConfigPath', () => {
  const originalEnvValue = process.env.MCPORTER_CONFIG;
  let tempDirs: string[] = [];
  let homedirSpy: { mockRestore(): void } | undefined;

  afterEach(() => {
    if (originalEnvValue === undefined) {
      delete process.env.MCPORTER_CONFIG;
    } else {
      process.env.MCPORTER_CONFIG = originalEnvValue;
    }
    homedirSpy?.mockRestore();
    for (const dir of tempDirs) {
      fs.rmSync(dir, { recursive: true, force: true });
    }
    tempDirs = [];
  });

  it('prefers an explicit config path argument', () => {
    const tempRoot = makeTempDir('mcporter-config-');
    tempDirs.push(tempRoot);
    const explicitPath = path.join(tempRoot, 'custom.json');
    fs.writeFileSync(explicitPath, '{"mcpServers":{}}');

    const resolved = resolveConfigPath(explicitPath, tempRoot);
    expect(resolved.path).toBe(explicitPath);
    expect(resolved.explicit).toBe(true);
  });

  it('uses MCPORTER_CONFIG when set', () => {
    const tempRoot = makeTempDir('mcporter-config-env-');
    tempDirs.push(tempRoot);
    const envPath = path.join(tempRoot, 'env-config.json');
    fs.writeFileSync(envPath, '{"mcpServers":{}}');
    process.env.MCPORTER_CONFIG = envPath;

    const resolved = resolveConfigPath(undefined, tempRoot);
    expect(resolved.path).toBe(envPath);
    expect(resolved.explicit).toBe(true);
  });

  it('falls back to the project config when it exists', () => {
    const tempRoot = makeTempDir('mcporter-project-');
    tempDirs.push(tempRoot);
    const projectConfig = path.join(tempRoot, 'config', 'mcporter.json');
    fs.mkdirSync(path.dirname(projectConfig), { recursive: true });
    fs.writeFileSync(projectConfig, '{"mcpServers":{}}');

    const resolved = resolveConfigPath(undefined, tempRoot);
    expect(resolved.path).toBe(projectConfig);
    expect(resolved.explicit).toBe(false);
  });

  it('falls back to the home config when project config is missing', () => {
    const tempRoot = makeTempDir('mcporter-project-missing-');
    const fakeHome = makeTempDir('mcporter-home-');
    tempDirs.push(tempRoot, fakeHome);
    const homeConfigDir = path.join(fakeHome, '.mcporter');
    const homeConfigPath = path.join(homeConfigDir, 'mcporter.json');
    fs.mkdirSync(homeConfigDir, { recursive: true });
    fs.writeFileSync(homeConfigPath, '{"mcpServers":{}}');
    homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue(fakeHome);

    const resolved = resolveConfigPath(undefined, tempRoot);
    expect(resolved.path).toBe(homeConfigPath);
    expect(resolved.explicit).toBe(false);
  });

  it('returns explicit=false when no config exists anywhere (regression for ENOENT bug)', () => {
    // This test verifies the fix for the bug where mcporter crashed with ENOENT
    // when run in an environment with no config file at all. The CLI was passing
    // the resolved (but non-existent) path as if it were explicit.
    const tempRoot = makeTempDir('mcporter-no-config-anywhere-');
    const fakeHome = makeTempDir('mcporter-empty-home-');
    tempDirs.push(tempRoot, fakeHome);
    // No config files created - completely empty environment
    homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue(fakeHome);

    const resolved = resolveConfigPath(undefined, tempRoot);
    // Should return the default project path but marked as NOT explicit
    expect(resolved.path).toBe(path.join(tempRoot, 'config', 'mcporter.json'));
    expect(resolved.explicit).toBe(false);
  });
});
