import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import { stripAnthropicEnv } from '../../../src/utils/shell-executor';

// We need to mock process.platform for cross-platform testing
const originalPlatform = process.platform;

describe('escapeShellArg', () => {
  describe('Unix (non-Windows)', () => {
    beforeEach(() => {
      Object.defineProperty(process, 'platform', { value: 'linux' });
    });
    afterEach(() => {
      Object.defineProperty(process, 'platform', { value: originalPlatform });
    });

    it('wraps argument in double quotes', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('arg')).toBe('"arg"');
    });

    it('escapes inner double quotes with backslash', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('say "hello"')).toBe('"say \\"hello\\""');
    });

    it('handles paths with spaces', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('/path/to/my file')).toBe('"/path/to/my file"');
    });

    it('handles empty string', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('')).toBe('""');
    });
  });

  describe('Windows (cmd.exe)', () => {
    beforeEach(() => {
      Object.defineProperty(process, 'platform', { value: 'win32' });
    });
    afterEach(() => {
      Object.defineProperty(process, 'platform', { value: originalPlatform });
    });

    it('wraps argument in double quotes', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('arg')).toBe('"arg"');
    });

    it('escapes inner double quotes by doubling them', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('say "hello"')).toBe('"say ""hello"""');
    });

    it('escapes percent signs', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('%PATH%')).toBe('"%%PATH%%"');
    });

    it('escapes caret characters', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('a^b')).toBe('"a^^b"');
    });

    it('replaces newlines with spaces', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('line1\nline2')).toBe('"line1 line2"');
    });

    it('replaces tabs with spaces', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('col1\tcol2')).toBe('"col1 col2"');
    });

    it('handles Windows paths with spaces', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('C:\\Program Files\\App')).toBe('"C:\\Program Files\\App"');
    });

    it('escapes exclamation marks for delayed expansion', async () => {
      const { escapeShellArg } = await import('../../../src/utils/shell-executor');
      expect(escapeShellArg('hello!')).toBe('"hello^^!"');
    });

    it('prefers ComSpec when resolving the escaped command shell', async () => {
      const originalComSpec = process.env.ComSpec;
      const originalCOMSPEC = process.env.COMSPEC;

      try {
        process.env.ComSpec = 'C:\\Windows\\System32\\cmd.exe';
        delete process.env.COMSPEC;
        const { getWindowsEscapedCommandShell } = await import(
          '../../../src/utils/shell-executor'
        );
        expect(getWindowsEscapedCommandShell()).toBe('C:\\Windows\\System32\\cmd.exe');
      } finally {
        if (originalComSpec === undefined) delete process.env.ComSpec;
        else process.env.ComSpec = originalComSpec;
        if (originalCOMSPEC === undefined) delete process.env.COMSPEC;
        else process.env.COMSPEC = originalCOMSPEC;
      }
    });

    it('falls back to cmd.exe when ComSpec is unavailable', async () => {
      const originalComSpec = process.env.ComSpec;
      const originalCOMSPEC = process.env.COMSPEC;

      try {
        delete process.env.ComSpec;
        delete process.env.COMSPEC;
        const { getWindowsEscapedCommandShell } = await import(
          '../../../src/utils/shell-executor'
        );
        expect(getWindowsEscapedCommandShell()).toBe('cmd.exe');
      } finally {
        if (originalComSpec === undefined) delete process.env.ComSpec;
        else process.env.ComSpec = originalComSpec;
        if (originalCOMSPEC === undefined) delete process.env.COMSPEC;
        else process.env.COMSPEC = originalCOMSPEC;
      }
    });
  });
});

describe('getWindowsEscapedCommandShell', () => {
  afterEach(() => {
    Object.defineProperty(process, 'platform', { value: originalPlatform });
  });

  it('returns shell=true outside Windows if called defensively', async () => {
    Object.defineProperty(process, 'platform', { value: 'linux' });
    const { getWindowsEscapedCommandShell } = await import('../../../src/utils/shell-executor');
    expect(getWindowsEscapedCommandShell()).toBe(true);
  });
});

describe('stripAnthropicEnv', () => {
  it('removes all ANTHROPIC_* keys', () => {
    const input = {
      PATH: '/usr/bin',
      ANTHROPIC_BASE_URL: 'http://localhost:3000',
      ANTHROPIC_MODEL: 'claude-3',
      HOME: '/home/user',
    };
    const result = stripAnthropicEnv(input);
    expect(result).toEqual({ PATH: '/usr/bin', HOME: '/home/user' });
  });

  it('preserves non-ANTHROPIC keys', () => {
    const input = { FOO: 'bar', ANTHROPIC_KEY: 'secret' };
    const result = stripAnthropicEnv(input);
    expect(result).toEqual({ FOO: 'bar' });
  });

  it('handles empty object', () => {
    expect(stripAnthropicEnv({})).toEqual({});
  });

  it('preserves undefined values', () => {
    const input: NodeJS.ProcessEnv = { FOO: 'bar', BAZ: undefined };
    const result = stripAnthropicEnv(input);
    expect(result.FOO).toBe('bar');
    expect(result.BAZ).toBeUndefined();
    expect('BAZ' in result).toBe(true);
  });

  it('is case-sensitive (only uppercase ANTHROPIC_)', () => {
    const input = {
      anthropic_base_url: 'lowercase',
      Anthropic_Model: 'mixed',
      ANTHROPIC_API_KEY: 'uppercase',
    };
    const result = stripAnthropicEnv(input);
    expect(result).toEqual({
      anthropic_base_url: 'lowercase',
      Anthropic_Model: 'mixed',
    });
  });

  it('strips all ANTHROPIC_ prefixed vars including nested names', () => {
    const input = {
      ANTHROPIC_: 'empty suffix',
      ANTHROPIC_V2_SETTING: 'v2',
      ANTHROPIC_INTERNAL_DEBUG: 'internal',
      PATH: '/bin',
    };
    const result = stripAnthropicEnv(input);
    expect(result).toEqual({ PATH: '/bin' });
  });
});
