const fc = require('fast-check');
const platformUtils = require('./platform-utils');

/**
 * Feature: dashcaddy-installer, Property 1: OS Detection Accuracy
 * For any supported platform (Windows, macOS, Linux), the installer should 
 * correctly detect the operating system and architecture.
 * Validates: Requirements 1.1, 14.1, 14.2, 14.3
 */
describe('Property 1: OS Detection Accuracy', () => {
  test('detectOS returns valid OS for any platform value', () => {
    fc.assert(
      fc.property(
        fc.constantFrom('win32', 'darwin', 'linux', 'freebsd', 'openbsd', 'sunos', 'aix'),
        (platform) => {
          const originalPlatform = process.platform;
          Object.defineProperty(process, 'platform', { value: platform, configurable: true });
          
          const detectedOS = platformUtils.detectOS();
          const validOSes = ['windows', 'macos', 'linux', 'unknown'];
          const isValid = validOSes.includes(detectedOS);
          
          // Restore original platform
          Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
          
          return isValid;
        }
      ),
      { numRuns: 100 }
    );
  });

  test('detectOS maps supported platforms correctly', () => {
    const platformMappings = [
      { platform: 'win32', expected: 'windows' },
      { platform: 'darwin', expected: 'macos' },
      { platform: 'linux', expected: 'linux' }
    ];

    platformMappings.forEach(({ platform, expected }) => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', { value: platform, configurable: true });
      
      expect(platformUtils.detectOS()).toBe(expected);
      
      Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
    });
  });

  test('detectArchitecture always returns a non-empty string', () => {
    fc.assert(
      fc.property(
        fc.constant(null), // We don't need to generate anything, just run the test
        () => {
          const arch = platformUtils.detectArchitecture();
          return typeof arch === 'string' && arch.length > 0;
        }
      ),
      { numRuns: 100 }
    );
  });

  test('getPlatformInfo returns consistent data structure', () => {
    fc.assert(
      fc.property(
        fc.constant(null),
        () => {
          const info = platformUtils.getPlatformInfo();
          
          // Check all required fields exist
          const hasAllFields = 
            typeof info.os === 'string' &&
            typeof info.arch === 'string' &&
            typeof info.shell === 'string' &&
            typeof info.platform === 'string' &&
            typeof info.hostname === 'string' &&
            typeof info.homedir === 'string' &&
            typeof info.defaultInstallPath === 'string' &&
            typeof info.userInstallPath === 'string';
          
          // Check that OS is valid
          const validOSes = ['windows', 'macos', 'linux', 'unknown'];
          const hasValidOS = validOSes.includes(info.os);
          
          // Check that shell is valid
          const validShells = ['powershell', 'bash', 'zsh'];
          const hasValidShell = validShells.includes(info.shell);
          
          return hasAllFields && hasValidOS && hasValidShell;
        }
      ),
      { numRuns: 100 }
    );
  });

  test('platform detection is deterministic', () => {
    fc.assert(
      fc.property(
        fc.constant(null),
        () => {
          const info1 = platformUtils.getPlatformInfo();
          const info2 = platformUtils.getPlatformInfo();
          
          // Same platform should return same results
          return (
            info1.os === info2.os &&
            info1.arch === info2.arch &&
            info1.shell === info2.shell &&
            info1.platform === info2.platform
          );
        }
      ),
      { numRuns: 100 }
    );
  });
});

/**
 * Feature: dashcaddy-installer, Property 18: Platform-Specific Path Handling
 * For any detected operating system, the installer should use the correct 
 * default installation path and path separator for that platform.
 * Validates: Requirements 2.1, 14.1, 14.2, 14.3, 14.6
 */
describe('Property 18: Platform-Specific Path Handling', () => {
  test('getDefaultInstallPath returns platform-appropriate paths', () => {
    const testCases = [
      { platform: 'win32', shouldContain: 'C:\\', shouldNotContain: '/' },
      { platform: 'darwin', shouldContain: '/Applications', shouldNotContain: '\\' },
      { platform: 'linux', shouldContain: '/opt', shouldNotContain: '\\' }
    ];

    testCases.forEach(({ platform, shouldContain, shouldNotContain }) => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', { value: platform, configurable: true });
      
      const path = platformUtils.getDefaultInstallPath();
      expect(path).toContain(shouldContain);
      if (shouldNotContain) {
        expect(path).not.toContain(shouldNotContain);
      }
      
      Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
    });
  });

  test('normalizePath handles any path string', () => {
    fc.assert(
      fc.property(
        fc.string(),
        (pathStr) => {
          try {
            const normalized = platformUtils.normalizePath(pathStr);
            return typeof normalized === 'string';
          } catch (error) {
            // Some strings might be invalid paths, that's okay
            return true;
          }
        }
      ),
      { numRuns: 100 }
    );
  });

  test('joinPath produces valid paths for any segments', () => {
    fc.assert(
      fc.property(
        fc.array(fc.string({ minLength: 1, maxLength: 20 }), { minLength: 1, maxLength: 5 }),
        (segments) => {
          try {
            const joined = platformUtils.joinPath(...segments);
            return typeof joined === 'string' && joined.length > 0;
          } catch (error) {
            // Some combinations might be invalid, that's okay
            return true;
          }
        }
      ),
      { numRuns: 100 }
    );
  });

  test('getUserInstallPath always includes DashCaddy', () => {
    fc.assert(
      fc.property(
        fc.constant(null),
        () => {
          const userPath = platformUtils.getUserInstallPath();
          return userPath.includes('DashCaddy');
        }
      ),
      { numRuns: 100 }
    );
  });
});

/**
 * Feature: dashcaddy-installer, Property 19: Platform-Specific Shell Selection
 * For any detected operating system, the installer should use the appropriate 
 * shell (PowerShell on Windows, bash/zsh on Unix-like systems) for executing commands.
 * Validates: Requirements 14.7
 */
describe('Property 19: Platform-Specific Shell Selection', () => {
  test('detectShell returns valid shell for any platform', () => {
    fc.assert(
      fc.property(
        fc.constantFrom('win32', 'darwin', 'linux'),
        (platform) => {
          const originalPlatform = process.platform;
          Object.defineProperty(process, 'platform', { value: platform, configurable: true });
          
          const shell = platformUtils.detectShell();
          const validShells = ['powershell', 'bash', 'zsh'];
          const isValid = validShells.includes(shell);
          
          Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
          
          return isValid;
        }
      ),
      { numRuns: 100 }
    );
  });

  test('Windows always uses PowerShell', () => {
    const originalPlatform = process.platform;
    Object.defineProperty(process, 'platform', { value: 'win32', configurable: true });
    
    expect(platformUtils.detectShell()).toBe('powershell');
    
    Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
  });

  test('Unix-like systems use bash or zsh', () => {
    const unixPlatforms = ['darwin', 'linux'];
    
    unixPlatforms.forEach(platform => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', { value: platform, configurable: true });
      
      const shell = platformUtils.detectShell();
      expect(['bash', 'zsh']).toContain(shell);
      
      Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
    });
  });

  test('getCommandExecution returns valid structure for any command', () => {
    fc.assert(
      fc.property(
        fc.string({ minLength: 1, maxLength: 100 }),
        (command) => {
          const exec = platformUtils.getCommandExecution(command);
          
          return (
            typeof exec === 'object' &&
            typeof exec.shell === 'string' &&
            Array.isArray(exec.args) &&
            exec.args.length > 0 &&
            exec.args.includes(command)
          );
        }
      ),
      { numRuns: 100 }
    );
  });

  test('getCommandExecution uses correct shell for platform', () => {
    const testCases = [
      { platform: 'win32', expectedShell: 'powershell.exe' },
      { platform: 'darwin', expectedShells: ['bash', 'zsh'] },
      { platform: 'linux', expectedShells: ['bash', 'zsh'] }
    ];

    testCases.forEach(({ platform, expectedShell, expectedShells }) => {
      const originalPlatform = process.platform;
      Object.defineProperty(process, 'platform', { value: platform, configurable: true });
      
      const exec = platformUtils.getCommandExecution('test');
      
      if (expectedShell) {
        expect(exec.shell).toBe(expectedShell);
      } else if (expectedShells) {
        expect(expectedShells).toContain(exec.shell);
      }
      
      Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true });
    });
  });
});
