const DependencyChecker = require('./dependency-checker');
const { exec } = require('child_process');

// Mock child_process
jest.mock('child_process');

describe('DependencyChecker', () => {
  let checker;

  beforeEach(() => {
    checker = new DependencyChecker();
    jest.clearAllMocks();
    // Default: commands fail (prevents hanging when unmocked calls occur)
    exec.mockImplementation((cmd, opts, callback) => {
      callback(new Error('command not found'), { stdout: '', stderr: 'command not found' });
    });
  });

  describe('checkDocker', () => {
    test('detects Docker installed and running', async () => {
      // Mock successful docker --version
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'Docker version 24.0.7, build afdd53b', stderr: '' });
      });
      
      // Mock successful docker info
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'Server Version: 24.0.7', stderr: '' });
      });

      const result = await checker.checkDocker();

      expect(result.installed).toBe(true);
      expect(result.running).toBe(true);
      expect(result.version).toBe('24.0.7');
    });

    test('detects Docker installed but not running', async () => {
      // Mock successful docker --version
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'Docker version 24.0.7, build afdd53b', stderr: '' });
      });
      
      // Mock failed docker info
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(new Error('Cannot connect to Docker daemon'), { stdout: '', stderr: 'Cannot connect' });
      });

      const result = await checker.checkDocker();

      expect(result.installed).toBe(true);
      expect(result.running).toBe(false);
      expect(result.version).toBe('24.0.7');
    });

    test('detects Docker not installed', async () => {
      // Mock failed docker --version
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(new Error('command not found'), { stdout: '', stderr: 'command not found' });
      });

      const result = await checker.checkDocker();

      expect(result.installed).toBe(false);
      expect(result.running).toBe(false);
      expect(result.version).toBeNull();
    });
  });

  describe('checkCaddy', () => {
    test('detects Caddy installed', async () => {
      // Mock successful caddy version
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'v2.7.6 h1:w0NymbG2m9PcvKWsrXO6EEkY9Ru4FJK8uQbYcev1p3A=', stderr: '' });
      });
      
      // Mock successful which/where caddy
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: '/usr/bin/caddy', stderr: '' });
      });

      const result = await checker.checkCaddy();

      expect(result.installed).toBe(true);
      expect(result.version).toBe('2.7.6');
      expect(result.path).toBe('/usr/bin/caddy');
    });

    test('detects Caddy not installed', async () => {
      // Mock failed caddy version
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(new Error('command not found'), { stdout: '', stderr: 'command not found' });
      });

      const result = await checker.checkCaddy();

      expect(result.installed).toBe(false);
      expect(result.version).toBeNull();
      expect(result.path).toBeNull();
    });
  });

  describe('getDockerInstallInstructions', () => {
    test('returns Windows instructions', () => {
      const instructions = checker.getDockerInstallInstructions('windows');

      expect(instructions.title).toContain('Windows');
      expect(instructions.steps).toBeInstanceOf(Array);
      expect(instructions.steps.length).toBeGreaterThan(0);
      expect(instructions.requiresWSL2).toBe(true);
      expect(instructions.url).toContain('docker.com');
    });

    test('returns macOS instructions', () => {
      const instructions = checker.getDockerInstallInstructions('macos');

      expect(instructions.title).toContain('Mac');
      expect(instructions.steps).toBeInstanceOf(Array);
      expect(instructions.requiresWSL2).toBe(false);
      expect(instructions.url).toContain('docker.com');
    });

    test('returns Linux instructions', () => {
      const instructions = checker.getDockerInstallInstructions('linux');

      expect(instructions.title).toContain('Linux');
      expect(instructions.steps).toBeInstanceOf(Array);
      expect(instructions.packageManagers).toBeDefined();
      expect(instructions.packageManagers.ubuntu).toBeDefined();
    });
  });

  describe('getCaddyInstallInstructions', () => {
    test('returns Windows instructions', () => {
      const instructions = checker.getCaddyInstallInstructions('windows');

      expect(instructions.title).toContain('Windows');
      expect(instructions.steps).toBeInstanceOf(Array);
      expect(instructions.automated).toBe(false);
      expect(instructions.url).toContain('caddyserver.com');
    });

    test('returns macOS instructions with automation', () => {
      const instructions = checker.getCaddyInstallInstructions('macos');

      expect(instructions.title).toContain('macOS');
      expect(instructions.automated).toBe(true);
      expect(instructions.command).toBe('brew install caddy');
    });

    test('returns Linux instructions with package managers', () => {
      const instructions = checker.getCaddyInstallInstructions('linux');

      expect(instructions.title).toContain('Linux');
      expect(instructions.automated).toBe(true);
      expect(instructions.packageManagers).toBeDefined();
      expect(instructions.packageManagers.ubuntu).toBeDefined();
      expect(instructions.packageManagers.fedora).toBeDefined();
    });
  });

  describe('installDocker', () => {
    test('returns manual installation instructions', async () => {
      const result = await checker.installDocker('windows');

      expect(result.success).toBe(false);
      expect(result.automated).toBe(false);
      expect(result.instructions).toBeDefined();
      expect(result.message).toContain('manual');
    });
  });

  describe('installCaddy', () => {
    test('returns manual instructions for Windows', async () => {
      const result = await checker.installCaddy('windows');

      expect(result.success).toBe(false);
      expect(result.automated).toBe(false);
      expect(result.instructions).toBeDefined();
    });

    test('attempts automated installation on macOS', async () => {
      // Mock successful brew install
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'Caddy installed', stderr: '' });
      });
      
      // Mock successful caddy version check
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'v2.7.6', stderr: '' });
      });
      
      // Mock successful which caddy
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: '/usr/local/bin/caddy', stderr: '' });
      });

      const result = await checker.installCaddy('macos');

      expect(result.automated).toBe(true);
      // Note: success depends on mocked execution
    });
  });

  describe('executeCommand', () => {
    test('executes command successfully', async () => {
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'success', stderr: '' });
      });

      const result = await checker.executeCommand('test command');

      expect(result.success).toBe(true);
      expect(result.stdout).toBe('success');
    });

    test('handles command failure', async () => {
      const error = new Error('command failed');
      error.stderr = 'error message';
      
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(error);
      });

      const result = await checker.executeCommand('test command');

      expect(result.success).toBe(false);
      expect(result.stderr).toContain('error');
    });
  });

  describe('detectLinuxDistro', () => {
    test('detects Ubuntu', async () => {
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'ID=ubuntu\nNAME="Ubuntu"', stderr: '' });
      });

      const distro = await checker.detectLinuxDistro();

      expect(distro).toBe('ubuntu');
    });

    test('detects Fedora', async () => {
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'ID=fedora\nNAME="Fedora"', stderr: '' });
      });

      const distro = await checker.detectLinuxDistro();

      expect(distro).toBe('fedora');
    });

    test('returns unknown for unrecognized distro', async () => {
      exec.mockImplementationOnce((cmd, opts, callback) => {
        callback(null, { stdout: 'ID=unknown', stderr: '' });
      });

      const distro = await checker.detectLinuxDistro();

      expect(distro).toBe('unknown');
    });
  });
});
