/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

/// <reference types="vitest/globals" />

// Mock 'os' first.
import * as osActual from 'node:os'; // Import for type info for the mock factory

vi.mock('os', async (importOriginal) => {
  const actualOs = await importOriginal<typeof osActual>();
  return {
    ...actualOs,
    homedir: vi.fn(() => path.resolve('/mock/home/user')),
    platform: vi.fn(() => 'linux'),
  };
});

// Mock './settings.js' to ensure it uses the mocked 'os.homedir()' for its internal constants.
vi.mock('./settings.js', async (importActual) => {
  const originalModule = await importActual<typeof import('./settings.js')>();
  return {
    __esModule: true, // Ensure correct module shape
    ...originalModule, // Re-export all original members
    // We are relying on originalModule's USER_SETTINGS_PATH being constructed with mocked os.homedir()
  };
});

// Mock trustedFolders
import * as trustedFolders from './trustedFolders.js';
vi.mock('./trustedFolders.js', () => ({
  isWorkspaceTrusted: vi.fn(),
  isFolderTrustEnabled: vi.fn(),
  loadTrustedFolders: vi.fn(),
}));

vi.mock('./settingsSchema.js', async (importOriginal) => {
  const actual = await importOriginal<typeof import('./settingsSchema.js')>();
  return {
    ...actual,
    getSettingsSchema: vi.fn(actual.getSettingsSchema),
  };
});

// NOW import everything else, including the (now effectively re-exported) settings.js
import * as path from 'node:path'; // Restored for MOCK_WORKSPACE_SETTINGS_PATH
import {
  describe,
  it,
  expect,
  vi,
  beforeEach,
  afterEach,
  type Mocked,
  type Mock,
} from 'vitest';
import * as fs from 'node:fs'; // fs will be mocked separately
import stripJsonComments from 'strip-json-comments'; // Will be mocked separately
import { isWorkspaceTrusted } from './trustedFolders.js';

// These imports will get the versions from the vi.mock('./settings.js', ...) factory.
import {
  loadSettings,
  USER_SETTINGS_PATH, // This IS the mocked path.
  getSystemSettingsPath,
  getSystemDefaultsPath,
  type Settings,
  type SettingsFile,
  saveSettings,
  getDefaultsFromSchema,
  loadEnvironment,
  migrateDeprecatedSettings,
  SettingScope,
  LoadedSettings,
  sanitizeEnvVar,
  createTestMergedSettings,
  resetSettingsCacheForTesting,
} from './settings.js';
import {
  FatalConfigError,
  GEMINI_DIR,
  Storage,
  AuthType,
  type MCPServerConfig,
} from '@google/gemini-cli-core';
import { updateSettingsFilePreservingFormat } from '../utils/commentJson.js';
import {
  getSettingsSchema,
  MergeStrategy,
  type SettingsSchema,
} from './settingsSchema.js';
import { createMockSettings } from '../test-utils/settings.js';

const MOCK_WORKSPACE_DIR = path.resolve(path.resolve('/mock/workspace'));
// Use the (mocked) GEMINI_DIR for consistency
const MOCK_WORKSPACE_SETTINGS_PATH = path.join(
  MOCK_WORKSPACE_DIR,
  GEMINI_DIR,
  'settings.json',
);

// A more flexible type for test data that allows arbitrary properties.
type TestSettings = Settings & { [key: string]: unknown };

// Helper to normalize paths for test assertions, making them OS-agnostic
const normalizePath = (p: string | fs.PathOrFileDescriptor) =>
  path.normalize(p.toString());

vi.mock('fs', async (importOriginal) => {
  // Get all the functions from the real 'fs' module
  const actualFs = await importOriginal<typeof fs>();

  return {
    ...actualFs, // Keep all the real functions
    // Now, just override the ones we need for the test
    existsSync: vi.fn(),
    readFileSync: vi.fn(),
    writeFileSync: vi.fn(),
    mkdirSync: vi.fn(),
    realpathSync: vi.fn((p: string) => p),
  };
});

vi.mock('./extension.js');

const mockCoreEvents = vi.hoisted(() => ({
  emitFeedback: vi.fn(),
  emitSettingsChanged: vi.fn(),
}));

vi.mock('@google/gemini-cli-core', async (importOriginal) => {
  const actual =
    await importOriginal<typeof import('@google/gemini-cli-core')>();
  const os = await import('node:os');
  const pathMod = await import('node:path');
  const fsMod = await import('node:fs');

  // Helper to resolve paths using the test's mocked environment
  const testResolve = (p: string | undefined) => {
    if (!p) return '';
    try {
      // Use the mocked fs.realpathSync if available, otherwise fallback
      return fsMod.realpathSync(pathMod.resolve(p));
    } catch {
      return pathMod.resolve(p);
    }
  };

  // Create a smarter mock for isWorkspaceHomeDir
  vi.spyOn(actual.Storage.prototype, 'isWorkspaceHomeDir').mockImplementation(
    function (this: Storage) {
      const target = testResolve(pathMod.dirname(this.getGeminiDir()));
      // Pick up the mocked home directory specifically from the 'os' mock
      const home = testResolve(os.homedir());
      return actual.normalizePath(target) === actual.normalizePath(home);
    },
  );

  return {
    ...actual,
    coreEvents: mockCoreEvents,
    homedir: vi.fn(() => os.homedir()),
  };
});

vi.mock('../utils/commentJson.js', () => ({
  updateSettingsFilePreservingFormat: vi.fn(),
}));

vi.mock('strip-json-comments', () => ({
  default: vi.fn((content) => content),
}));

describe('Settings Loading and Merging', () => {
  let mockFsExistsSync: Mocked<typeof fs.existsSync>;
  let mockStripJsonComments: Mocked<typeof stripJsonComments>;
  let mockFsMkdirSync: Mocked<typeof fs.mkdirSync>;

  beforeEach(() => {
    vi.resetAllMocks();
    resetSettingsCacheForTesting();

    mockFsExistsSync = vi.mocked(fs.existsSync);
    mockFsMkdirSync = vi.mocked(fs.mkdirSync);
    mockStripJsonComments = vi.mocked(stripJsonComments);

    vi.mocked(osActual.homedir).mockReturnValue(
      path.resolve('/mock/home/user'),
    );
    (mockStripJsonComments as unknown as Mock).mockImplementation(
      (jsonString: string) => jsonString,
    );
    (mockFsExistsSync as Mock).mockReturnValue(false);
    (fs.readFileSync as Mock).mockReturnValue('{}'); // Return valid empty JSON
    (mockFsMkdirSync as Mock).mockImplementation(() => undefined);
    vi.spyOn(trustedFolders, 'isWorkspaceTrusted').mockReturnValue({
      isTrusted: true,
      source: 'file',
    });
  });

  afterEach(() => {
    vi.restoreAllMocks();
    vi.unstubAllEnvs();
  });

  describe('loadSettings', () => {
    it.each([
      {
        scope: 'system',
        path: getSystemSettingsPath(),
        content: {
          ui: { theme: 'system-default' },
          tools: { sandbox: false },
        },
      },
      {
        scope: 'user',
        path: USER_SETTINGS_PATH,
        content: {
          ui: { theme: 'dark' },
          context: { fileName: 'USER_CONTEXT.md' },
        },
      },
      {
        scope: 'workspace',
        path: MOCK_WORKSPACE_SETTINGS_PATH,
        content: {
          tools: { sandbox: true },
          context: { fileName: 'WORKSPACE_CONTEXT.md' },
        },
      },
    ])(
      'should load $scope settings if only $scope file exists',
      ({ scope, path: p, content }) => {
        (mockFsExistsSync as Mock).mockImplementation(
          (pathLike: fs.PathLike) =>
            path.normalize(pathLike.toString()) === path.normalize(p),
        );
        (fs.readFileSync as Mock).mockImplementation(
          (pathDesc: fs.PathOrFileDescriptor) => {
            if (path.normalize(pathDesc.toString()) === path.normalize(p))
              return JSON.stringify(content);
            return '{}';
          },
        );

        const settings = loadSettings(MOCK_WORKSPACE_DIR);

        expect(fs.readFileSync).toHaveBeenCalledWith(
          expect.stringContaining(path.basename(p)),
          'utf-8',
        );
        expect(
          settings[scope as 'system' | 'user' | 'workspace'].settings,
        ).toEqual(content);
        expect(settings.merged).toMatchObject(content);
      },
    );

    it('should merge system, user and workspace settings, with system taking precedence over workspace, and workspace over user', () => {
      (mockFsExistsSync as Mock).mockImplementation((p: fs.PathLike) => {
        const normP = path.normalize(p.toString());
        return (
          normP === path.normalize(getSystemSettingsPath()) ||
          normP === path.normalize(USER_SETTINGS_PATH) ||
          normP === path.normalize(MOCK_WORKSPACE_SETTINGS_PATH)
        );
      });
      const systemSettingsContent = {
        ui: {
          theme: 'system-theme',
        },
        tools: {
          sandbox: false,
        },
        mcp: {
          allowed: ['server1', 'server2'],
        },
        telemetry: { enabled: false },
      };
      const userSettingsContent = {
        ui: {
          theme: 'dark',
        },
        tools: {
          sandbox: true,
        },
        context: {
          fileName: 'USER_CONTEXT.md',
        },
      };
      const workspaceSettingsContent = {
        tools: {
          sandbox: false,
          core: ['tool1'],
        },
        context: {
          fileName: 'WORKSPACE_CONTEXT.md',
        },
        mcp: {
          allowed: ['server1', 'server2', 'server3'],
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          const normP = path.normalize(p.toString());
          if (normP === path.normalize(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normP === path.normalize(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normP === path.normalize(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.system.settings).toEqual(systemSettingsContent);
      expect(settings.user.settings).toEqual(userSettingsContent);
      expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
      expect(settings.merged).toMatchObject({
        ui: {
          theme: 'system-theme',
        },
        tools: {
          sandbox: false,
          core: ['tool1'],
        },
        telemetry: { enabled: false },
        context: {
          fileName: 'WORKSPACE_CONTEXT.md',
        },
        mcp: {
          allowed: ['server1', 'server2'],
        },
      });
    });

    it('should merge all settings files with the correct precedence', () => {
      // Mock schema to test defaults application
      const mockSchema = {
        ui: { type: 'object', default: {}, properties: {} },
        tools: { type: 'object', default: {}, properties: {} },
        context: {
          type: 'object',
          default: {},
          properties: {
            discoveryMaxDirs: { type: 'number', default: 200 },
            includeDirectories: {
              type: 'array',
              default: [],
              mergeStrategy: MergeStrategy.CONCAT,
            },
          },
        },
        mcpServers: { type: 'object', default: {} },
      };

      (getSettingsSchema as Mock).mockReturnValue(
        mockSchema as unknown as SettingsSchema,
      );

      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemDefaultsContent = {
        ui: {
          theme: 'default-theme',
        },
        tools: {
          sandbox: true,
        },
        telemetry: true,
        context: {
          includeDirectories: ['/system/defaults/dir'],
        },
      };
      const userSettingsContent = {
        ui: {
          theme: 'user-theme',
        },
        context: {
          fileName: 'USER_CONTEXT.md',
          includeDirectories: ['/user/dir1', '/user/dir2'],
        },
      };
      const workspaceSettingsContent = {
        tools: {
          sandbox: false,
        },
        context: {
          fileName: 'WORKSPACE_CONTEXT.md',
          includeDirectories: ['/workspace/dir'],
        },
      };
      const systemSettingsContent = {
        ui: {
          theme: 'system-theme',
        },
        telemetry: false,
        context: {
          includeDirectories: ['/system/dir'],
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemDefaultsPath()))
            return JSON.stringify(systemDefaultsContent);
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.systemDefaults.settings).toEqual(systemDefaultsContent);
      expect(settings.system.settings).toEqual(systemSettingsContent);
      expect(settings.user.settings).toEqual(userSettingsContent);
      expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
      expect(settings.merged).toEqual({
        context: {
          discoveryMaxDirs: 200,
          includeDirectories: [
            '/system/defaults/dir',
            '/user/dir1',
            '/user/dir2',
            '/workspace/dir',
            '/system/dir',
          ],
          fileName: 'WORKSPACE_CONTEXT.md',
        },
        mcpServers: {},
        ui: { theme: 'system-theme' },
        tools: { sandbox: false },
        telemetry: false,
      });
    });

    it('should use folderTrust from workspace settings when trusted', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        security: {
          folderTrust: {
            enabled: true,
          },
        },
      };
      const workspaceSettingsContent = {
        security: {
          folderTrust: {
            enabled: false, // This should be used
          },
        },
      };
      const systemSettingsContent = {
        // No folderTrust here
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.security?.folderTrust?.enabled).toBe(false); // Workspace setting should be used
    });

    it('should resolve environment variables and cast them to correct types before validation', () => {
      vi.stubEnv('TEST_AUTO_THEME', 'false');
      vi.stubEnv('TEST_MAX_TURNS', '15');

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (
            path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH)
          ) {
            return JSON.stringify({
              ui: { autoThemeSwitching: '$TEST_AUTO_THEME' },
              model: { maxSessionTurns: '$TEST_MAX_TURNS' },
            });
          }
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.ui.autoThemeSwitching).toBe(false);
      expect(settings.merged.model.maxSessionTurns).toBe(15);
      expect(settings.errors).toHaveLength(0);
    });

    it('should use default values from environment variable placeholders', () => {
      vi.stubEnv('TEST_AUTO_THEME', ''); // Should trigger default
      delete process.env['TEST_AUTO_THEME'];

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (
            path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH)
          ) {
            return JSON.stringify({
              ui: { autoThemeSwitching: '${TEST_AUTO_THEME:-true}' },
            });
          }
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.ui.autoThemeSwitching).toBe(true);
      expect(settings.errors).toHaveLength(0);
    });

    it('should record validation errors if expansion result is invalid', () => {
      vi.stubEnv('TEST_MAX_TURNS', 'not-a-number');

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (
            path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH)
          ) {
            return JSON.stringify({
              model: { maxSessionTurns: '$TEST_MAX_TURNS' },
            });
          }
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.errors.length).toBeGreaterThan(0);
      expect(settings.errors[0].message).toContain(
        'Expected number, received string',
      );
      // Should fall back to the expanded string value
      expect(settings.merged.model.maxSessionTurns).toBe('not-a-number');
    });

    it('should preserve environment variable placeholders on save', () => {
      vi.stubEnv('TEST_AUTO_THEME', 'true');
      const placeholder = '${TEST_AUTO_THEME:-false}';

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (
            path.normalize(p.toString()) === path.normalize(USER_SETTINGS_PATH)
          ) {
            return JSON.stringify({
              ui: { autoThemeSwitching: placeholder },
            });
          }
          return '{}';
        },
      );

      // Load settings - this will expand the placeholder for runtime use
      const loaded = loadSettings(MOCK_WORKSPACE_DIR);
      expect(loaded.merged.ui.autoThemeSwitching).toBe(true);

      // Verify that the original settings for the user scope still have the placeholder
      const userFile = loaded.forScope(SettingScope.User);
      expect(userFile.originalSettings.ui?.autoThemeSwitching).toBe(
        placeholder,
      );

      // Save settings - this should use the originalSettings (with placeholders)
      const mockUpdate = vi.mocked(updateSettingsFilePreservingFormat);
      saveSettings(userFile);

      expect(mockUpdate).toHaveBeenCalledWith(
        USER_SETTINGS_PATH,
        expect.objectContaining({
          ui: expect.objectContaining({
            autoThemeSwitching: placeholder,
          }),
        }),
      );
    });

    it('should use system folderTrust over user setting', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        security: {
          folderTrust: {
            enabled: false,
          },
        },
      };
      const workspaceSettingsContent = {
        security: {
          folderTrust: {
            enabled: true, // This should be ignored
          },
        },
      };
      const systemSettingsContent = {
        security: {
          folderTrust: {
            enabled: true,
          },
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.security?.folderTrust?.enabled).toBe(true); // System setting should be used
    });

    it('should not allow user or workspace to override system disableYoloMode', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        security: {
          disableYoloMode: false,
          disableAlwaysAllow: false,
        },
      };
      const workspaceSettingsContent = {
        security: {
          disableYoloMode: false, // This should be ignored
          disableAlwaysAllow: false, // This should be ignored
        },
      };
      const systemSettingsContent = {
        security: {
          disableYoloMode: true,
          disableAlwaysAllow: true,
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.security?.disableYoloMode).toBe(true); // System setting should be used
      expect(settings.merged.security?.disableAlwaysAllow).toBe(true); // System setting should be used
    });

    it.each([
      {
        description: 'contextFileName in user settings',
        path: USER_SETTINGS_PATH,
        content: { context: { fileName: 'CUSTOM.md' } },
        expected: { key: 'context.fileName', value: 'CUSTOM.md' },
      },
      {
        description: 'contextFileName in workspace settings',
        path: MOCK_WORKSPACE_SETTINGS_PATH,
        content: { context: { fileName: 'PROJECT_SPECIFIC.md' } },
        expected: { key: 'context.fileName', value: 'PROJECT_SPECIFIC.md' },
      },
      {
        description: 'excludedProjectEnvVars in user settings',
        path: USER_SETTINGS_PATH,
        content: {
          advanced: { excludedEnvVars: ['DEBUG', 'NODE_ENV', 'CUSTOM_VAR'] },
        },
        expected: {
          key: 'advanced.excludedEnvVars',
          value: ['DEBUG', 'DEBUG_MODE', 'NODE_ENV', 'CUSTOM_VAR'],
        },
      },
      {
        description: 'excludedProjectEnvVars in workspace settings',
        path: MOCK_WORKSPACE_SETTINGS_PATH,
        content: {
          advanced: { excludedEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'] },
        },
        expected: {
          key: 'advanced.excludedEnvVars',
          value: ['DEBUG', 'DEBUG_MODE', 'WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
        },
      },
    ])(
      'should handle $description correctly',
      ({ path, content, expected }) => {
        (mockFsExistsSync as Mock).mockImplementation(
          (p: fs.PathLike) => normalizePath(p) === normalizePath(path),
        );
        (fs.readFileSync as Mock).mockImplementation(
          (p: fs.PathOrFileDescriptor) => {
            if (normalizePath(p) === normalizePath(path))
              return JSON.stringify(content);
            return '{}';
          },
        );

        const settings = loadSettings(MOCK_WORKSPACE_DIR);
        const keys = expected.key.split('.');
        let result: unknown = settings.merged;
        for (const key of keys) {
          result = (result as { [key: string]: unknown })[key];
        }
        expect(result).toEqual(expected.value);
      },
    );

    it('should merge excludedProjectEnvVars with workspace taking precedence over user', () => {
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH) ||
          normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH),
      );
      const userSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'] },
      };
      const workspaceSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'] },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.user.settings.advanced?.excludedEnvVars).toEqual([
        'DEBUG',
        'NODE_ENV',
        'USER_VAR',
      ]);
      expect(settings.workspace.settings.advanced?.excludedEnvVars).toEqual([
        'WORKSPACE_DEBUG',
        'WORKSPACE_VAR',
      ]);
      expect(settings.merged.advanced?.excludedEnvVars).toEqual([
        'DEBUG',
        'DEBUG_MODE',
        'NODE_ENV',
        'USER_VAR',
        'WORKSPACE_DEBUG',
        'WORKSPACE_VAR',
      ]);
    });

    it('should default contextFileName to undefined if not in any settings file', () => {
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH) ||
          normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH),
      );
      const userSettingsContent = { ui: { theme: 'dark' } };
      const workspaceSettingsContent = { tools: { sandbox: true } };
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.context?.fileName).toBeUndefined();
    });

    it.each([
      {
        scope: 'user',
        path: USER_SETTINGS_PATH,
        content: { telemetry: { enabled: true } },
        expected: true,
      },
      {
        scope: 'workspace',
        path: MOCK_WORKSPACE_SETTINGS_PATH,
        content: { telemetry: { enabled: false } },
        expected: false,
      },
    ])(
      'should load telemetry setting from $scope settings',
      ({ path, content, expected }) => {
        (mockFsExistsSync as Mock).mockImplementation(
          (p: fs.PathLike) => normalizePath(p) === normalizePath(path),
        );
        (fs.readFileSync as Mock).mockImplementation(
          (p: fs.PathOrFileDescriptor) => {
            if (normalizePath(p) === normalizePath(path))
              return JSON.stringify(content);
            return '{}';
          },
        );
        const settings = loadSettings(MOCK_WORKSPACE_DIR);
        expect(settings.merged.telemetry?.enabled).toBe(expected);
      },
    );

    it('should prioritize workspace telemetry setting over user setting', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = { telemetry: { enabled: true } };
      const workspaceSettingsContent = { telemetry: { enabled: false } };
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );
      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.telemetry?.enabled).toBe(false);
    });

    it('should have telemetry as undefined if not in any settings file', () => {
      (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
      (fs.readFileSync as Mock).mockReturnValue('{}');
      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.telemetry).toBeUndefined();
      expect(settings.merged.ui).toBeDefined();
      expect(settings.merged.mcpServers).toEqual({});
    });

    it('should merge MCP servers correctly, with workspace taking precedence', () => {
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH) ||
          normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH),
      );
      const userSettingsContent = {
        mcpServers: {
          'user-server': {
            command: 'user-command',
            args: ['--user-arg'],
            description: 'User MCP server',
          },
          'shared-server': {
            command: 'user-shared-command',
            description: 'User shared server config',
          },
        },
      };
      const workspaceSettingsContent = {
        mcpServers: {
          'workspace-server': {
            command: 'workspace-command',
            args: ['--workspace-arg'],
            description: 'Workspace MCP server',
          },
          'shared-server': {
            command: 'workspace-shared-command',
            description: 'Workspace shared server config',
          },
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.user.settings).toEqual(userSettingsContent);
      expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
      expect(settings.merged.mcpServers).toEqual({
        'user-server': {
          command: 'user-command',
          args: ['--user-arg'],
          description: 'User MCP server',
        },
        'workspace-server': {
          command: 'workspace-command',
          args: ['--workspace-arg'],
          description: 'Workspace MCP server',
        },
        'shared-server': {
          command: 'workspace-shared-command',
          description: 'Workspace shared server config',
        },
      });
    });

    it.each([
      {
        scope: 'user',
        path: USER_SETTINGS_PATH,
        content: {
          mcpServers: {
            'user-only-server': {
              command: 'user-only-command',
              description: 'User only server',
            },
          },
        },
        expected: {
          'user-only-server': {
            command: 'user-only-command',
            description: 'User only server',
          },
        },
      },
      {
        scope: 'workspace',
        path: MOCK_WORKSPACE_SETTINGS_PATH,
        content: {
          mcpServers: {
            'workspace-only-server': {
              command: 'workspace-only-command',
              description: 'Workspace only server',
            },
          },
        },
        expected: {
          'workspace-only-server': {
            command: 'workspace-only-command',
            description: 'Workspace only server',
          },
        },
      },
    ])(
      'should handle MCP servers when only in $scope settings',
      ({ path, content, expected }) => {
        (mockFsExistsSync as Mock).mockImplementation(
          (p: fs.PathLike) => normalizePath(p) === normalizePath(path),
        );
        (fs.readFileSync as Mock).mockImplementation(
          (p: fs.PathOrFileDescriptor) => {
            if (normalizePath(p) === normalizePath(path))
              return JSON.stringify(content);
            return '{}';
          },
        );

        const settings = loadSettings(MOCK_WORKSPACE_DIR);
        expect(settings.merged.mcpServers).toEqual(expected);
      },
    );

    it('should have mcpServers as undefined if not in any settings file', () => {
      (mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
      (fs.readFileSync as Mock).mockReturnValue('{}');
      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.mcpServers).toEqual({});
    });

    it('should merge MCP servers from system, user, and workspace with system taking precedence', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        mcpServers: {
          'shared-server': {
            command: 'system-command',
            args: ['--system-arg'],
          },
          'system-only-server': {
            command: 'system-only-command',
          },
        },
      };
      const userSettingsContent = {
        mcpServers: {
          'user-server': {
            command: 'user-command',
          },
          'shared-server': {
            command: 'user-command',
            description: 'from user',
          },
        },
      };
      const workspaceSettingsContent = {
        mcpServers: {
          'workspace-server': {
            command: 'workspace-command',
          },
          'shared-server': {
            command: 'workspace-command',
            args: ['--workspace-arg'],
          },
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.mcpServers).toEqual({
        'user-server': {
          command: 'user-command',
        },
        'workspace-server': {
          command: 'workspace-command',
        },
        'system-only-server': {
          command: 'system-only-command',
        },
        'shared-server': {
          command: 'system-command',
          args: ['--system-arg'],
        },
      });
    });

    it('should merge mcp allowed/excluded lists with system taking precedence over workspace', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        mcp: {
          allowed: ['system-allowed'],
        },
      };
      const userSettingsContent = {
        mcp: {
          allowed: ['user-allowed'],
          excluded: ['user-excluded'],
        },
      };
      const workspaceSettingsContent = {
        mcp: {
          allowed: ['workspace-allowed'],
          excluded: ['workspace-excluded'],
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.mcp).toEqual({
        allowed: ['system-allowed'],
        excluded: ['workspace-excluded'],
      });
    });

    describe('LoadedSettings MCP consolidation', () => {
      it('should consolidate mcp excluded list across all scopes', () => {
        const loaded = new LoadedSettings(
          {
            path: '',
            settings: { mcp: { excluded: ['system-excluded'] } },
            originalSettings: {},
          },
          {
            path: '',
            settings: { mcp: { excluded: ['defaults-excluded'] } },
            originalSettings: {},
          },
          {
            path: '',
            settings: { mcp: { excluded: ['user-excluded'] } },
            originalSettings: {},
          },
          {
            path: '',
            settings: { mcp: { excluded: ['workspace-excluded'] } },
            originalSettings: {},
          },
          true,
        );

        expect(loaded.getConsolidatedExcludedMcpServers()).toEqual([
          'system-excluded',
          'defaults-excluded',
          'user-excluded',
          'workspace-excluded',
        ]);
      });

      it('should consolidate allowed mcp list via case-insensitive intersection', () => {
        const loaded = new LoadedSettings(
          {
            path: '',
            settings: { mcp: { allowed: ['Server-A', 'Server-B'] } },
            originalSettings: {},
          },
          {
            path: '',
            settings: { mcp: { allowed: ['server-a', 'Server-C'] } },
            originalSettings: {},
          },
          { path: '', settings: {}, originalSettings: {} }, // no allowlist in user
          {
            path: '',
            settings: { mcp: { allowed: ['SERVER-A', 'Server-D'] } },
            originalSettings: {},
          },
          true,
        );

        expect(loaded.getConsolidatedAllowedMcpServers()).toEqual(['Server-A']);
      });

      it('should return undefined allowed list if no scopes define one', () => {
        const loaded = new LoadedSettings(
          { path: '', settings: {}, originalSettings: {} },
          { path: '', settings: {}, originalSettings: {} },
          { path: '', settings: {}, originalSettings: {} },
          { path: '', settings: {}, originalSettings: {} },
          true,
        );

        expect(loaded.getConsolidatedAllowedMcpServers()).toBeUndefined();
      });
    });

    describe('compressionThreshold settings', () => {
      it.each([
        {
          description:
            'should be taken from user settings if only present there',
          userContent: { model: { compressionThreshold: 0.5 } },
          workspaceContent: {},
          expected: 0.5,
        },
        {
          description:
            'should be taken from workspace settings if only present there',
          userContent: {},
          workspaceContent: { model: { compressionThreshold: 0.8 } },
          expected: 0.8,
        },
        {
          description:
            'should prioritize workspace settings over user settings',
          userContent: { model: { compressionThreshold: 0.5 } },
          workspaceContent: { model: { compressionThreshold: 0.8 } },
          expected: 0.8,
        },
        {
          description: 'should be default if not in any settings file',
          userContent: {},
          workspaceContent: {},
          expected: 0.5,
        },
      ])('$description', ({ userContent, workspaceContent, expected }) => {
        (mockFsExistsSync as Mock).mockReturnValue(true);
        (fs.readFileSync as Mock).mockImplementation(
          (p: fs.PathOrFileDescriptor) => {
            if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
              return JSON.stringify(userContent);
            if (
              normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH)
            )
              return JSON.stringify(workspaceContent);
            return '{}';
          },
        );

        const settings = loadSettings(MOCK_WORKSPACE_DIR);
        expect(settings.merged.model?.compressionThreshold).toEqual(expected);
      });
    });

    it('should use user compressionThreshold if workspace does not define it', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        general: {},
        model: { compressionThreshold: 0.5 },
      };
      const workspaceSettingsContent = {
        general: {},
        model: {},
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.model?.compressionThreshold).toEqual(0.5);
    });

    it('should merge includeDirectories from all scopes', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        context: { includeDirectories: ['/system/dir'] },
      };
      const systemDefaultsContent = {
        context: { includeDirectories: ['/system/defaults/dir'] },
      };
      const userSettingsContent = {
        context: { includeDirectories: ['/user/dir1', '/user/dir2'] },
      };
      const workspaceSettingsContent = {
        context: { includeDirectories: ['/workspace/dir'] },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath()))
            return JSON.stringify(systemSettingsContent);
          if (normalizePath(p) === normalizePath(getSystemDefaultsPath()))
            return JSON.stringify(systemDefaultsContent);
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.context?.includeDirectories).toEqual([
        '/system/defaults/dir',
        '/user/dir1',
        '/user/dir2',
        '/workspace/dir',
        '/system/dir',
      ]);
    });

    it('should handle JSON parsing errors gracefully', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true); // Both files "exist"
      const invalidJsonContent = 'invalid json';
      const userReadError = new SyntaxError(
        "Expected ',' or '}' after property value in JSON at position 10",
      );
      const workspaceReadError = new SyntaxError(
        'Unexpected token i in JSON at position 0',
      );

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH)) {
            // Simulate JSON.parse throwing for user settings
            vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
              throw userReadError;
            });
            return invalidJsonContent; // Content that would cause JSON.parse to throw
          }
          if (
            normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH)
          ) {
            // Simulate JSON.parse throwing for workspace settings
            vi.spyOn(JSON, 'parse').mockImplementationOnce(() => {
              throw workspaceReadError;
            });
            return invalidJsonContent;
          }
          return '{}'; // Default for other reads
        },
      );

      try {
        loadSettings(MOCK_WORKSPACE_DIR);
        throw new Error('loadSettings should have thrown a FatalConfigError');
      } catch (e) {
        expect(e).toBeInstanceOf(FatalConfigError);
        const error = e as FatalConfigError;
        expect(error.message).toContain(
          `Error in ${USER_SETTINGS_PATH}: ${userReadError.message}`,
        );
        expect(error.message).toContain(
          `Error in ${MOCK_WORKSPACE_SETTINGS_PATH}: ${workspaceReadError.message}`,
        );
        expect(error.message).toContain(
          'Please fix the configuration file(s) and try again.',
        );
      }

      // Restore JSON.parse mock if it was spied on specifically for this test
      vi.restoreAllMocks(); // Or more targeted restore if needed
    });

    it('should resolve environment variables in user settings', () => {
      process.env['TEST_API_KEY'] = 'user_api_key_from_env';
      const userSettingsContent: TestSettings = {
        apiKey: '$TEST_API_KEY',
        someUrl: 'https://test.com/${TEST_API_KEY}',
      };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.user.settings as TestSettings)['apiKey']).toBe(
        'user_api_key_from_env',
      );
      expect((settings.user.settings as TestSettings)['someUrl']).toBe(
        'https://test.com/user_api_key_from_env',
      );
      expect((settings.merged as TestSettings)['apiKey']).toBe(
        'user_api_key_from_env',
      );
      delete process.env['TEST_API_KEY'];
    });

    it('should resolve environment variables in workspace settings', () => {
      process.env['WORKSPACE_ENDPOINT'] = 'workspace_endpoint_from_env';
      const workspaceSettingsContent: TestSettings = {
        endpoint: '${WORKSPACE_ENDPOINT}/api',
        nested: { value: '$WORKSPACE_ENDPOINT' },
      };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.workspace.settings as TestSettings)['endpoint']).toBe(
        'workspace_endpoint_from_env/api',
      );
      const nested = (settings.workspace.settings as TestSettings)[
        'nested'
      ] as Record<string, unknown>;
      expect(nested['value']).toBe('workspace_endpoint_from_env');
      expect((settings.merged as TestSettings)['endpoint']).toBe(
        'workspace_endpoint_from_env/api',
      );
      delete process.env['WORKSPACE_ENDPOINT'];
    });

    it('should correctly resolve and merge env variables from different scopes', () => {
      process.env['SYSTEM_VAR'] = 'system_value';
      process.env['USER_VAR'] = 'user_value';
      process.env['WORKSPACE_VAR'] = 'workspace_value';
      process.env['SHARED_VAR'] = 'final_value';

      const systemSettingsContent: TestSettings = {
        configValue: '$SHARED_VAR',
        systemOnly: '$SYSTEM_VAR',
      };
      const userSettingsContent: TestSettings = {
        configValue: '$SHARED_VAR',
        userOnly: '$USER_VAR',
        ui: {
          theme: 'dark',
        },
      };
      const workspaceSettingsContent: TestSettings = {
        configValue: '$SHARED_VAR',
        workspaceOnly: '$WORKSPACE_VAR',
        ui: {
          theme: 'light',
        },
      };

      (mockFsExistsSync as Mock).mockReturnValue(true);
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH)) {
            return JSON.stringify(userSettingsContent);
          }
          if (
            normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH)
          ) {
            return JSON.stringify(workspaceSettingsContent);
          }
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      // Check resolved values in individual scopes
      expect((settings.system.settings as TestSettings)['configValue']).toBe(
        'final_value',
      );
      expect((settings.system.settings as TestSettings)['systemOnly']).toBe(
        'system_value',
      );
      expect((settings.user.settings as TestSettings)['configValue']).toBe(
        'final_value',
      );
      expect((settings.user.settings as TestSettings)['userOnly']).toBe(
        'user_value',
      );
      expect((settings.workspace.settings as TestSettings)['configValue']).toBe(
        'final_value',
      );
      expect(
        (settings.workspace.settings as TestSettings)['workspaceOnly'],
      ).toBe('workspace_value');

      // Check merged values (system > workspace > user)
      expect((settings.merged as TestSettings)['configValue']).toBe(
        'final_value',
      );
      expect((settings.merged as TestSettings)['systemOnly']).toBe(
        'system_value',
      );
      expect((settings.merged as TestSettings)['userOnly']).toBe('user_value');
      expect((settings.merged as TestSettings)['workspaceOnly']).toBe(
        'workspace_value',
      );
      expect(settings.merged.ui?.theme).toBe('light'); // workspace overrides user

      delete process.env['SYSTEM_VAR'];
      delete process.env['USER_VAR'];
      delete process.env['WORKSPACE_VAR'];
      delete process.env['SHARED_VAR'];
    });

    it('should correctly merge dnsResolutionOrder with workspace taking precedence', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        advanced: { dnsResolutionOrder: 'ipv4first' },
      };
      const workspaceSettingsContent = {
        advanced: { dnsResolutionOrder: 'verbatim' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.advanced?.dnsResolutionOrder).toBe('verbatim');
    });

    it('should use user dnsResolutionOrder if workspace is not defined', () => {
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      const userSettingsContent = {
        advanced: { dnsResolutionOrder: 'verbatim' },
      };
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.advanced?.dnsResolutionOrder).toBe('verbatim');
    });

    it('should leave unresolved environment variables as is', () => {
      const userSettingsContent: TestSettings = { apiKey: '$UNDEFINED_VAR' };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.user.settings as TestSettings)['apiKey']).toBe(
        '$UNDEFINED_VAR',
      );
      expect((settings.merged as TestSettings)['apiKey']).toBe(
        '$UNDEFINED_VAR',
      );
    });

    it('should resolve multiple environment variables in a single string', () => {
      process.env['VAR_A'] = 'valueA';
      process.env['VAR_B'] = 'valueB';
      const userSettingsContent: TestSettings = {
        path: '/path/$VAR_A/${VAR_B}/end',
      };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );
      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.user.settings as TestSettings)['path']).toBe(
        '/path/valueA/valueB/end',
      );
      delete process.env['VAR_A'];
      delete process.env['VAR_B'];
    });

    it('should resolve environment variables in arrays', () => {
      process.env['ITEM_1'] = 'item1_env';
      process.env['ITEM_2'] = 'item2_env';
      const userSettingsContent: TestSettings = {
        list: ['$ITEM_1', '${ITEM_2}', 'literal'],
      };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );
      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.user.settings as TestSettings)['list']).toEqual([
        'item1_env',
        'item2_env',
        'literal',
      ]);
      delete process.env['ITEM_1'];
      delete process.env['ITEM_2'];
    });

    it('should correctly pass through null, boolean, and number types, and handle undefined properties', () => {
      process.env['MY_ENV_STRING'] = 'env_string_value';
      process.env['MY_ENV_STRING_NESTED'] = 'env_string_nested_value';

      const userSettingsContent: TestSettings = {
        nullVal: null,
        trueVal: true,
        falseVal: false,
        numberVal: 123.45,
        stringVal: '$MY_ENV_STRING',
        nestedObj: {
          nestedNull: null,
          nestedBool: true,
          nestedNum: 0,
          nestedString: 'literal',
          anotherEnv: '${MY_ENV_STRING_NESTED}',
        },
      };

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect((settings.user.settings as TestSettings)['nullVal']).toBeNull();
      expect((settings.user.settings as TestSettings)['trueVal']).toBe(true);
      expect((settings.user.settings as TestSettings)['falseVal']).toBe(false);
      expect((settings.user.settings as TestSettings)['numberVal']).toBe(
        123.45,
      );
      expect((settings.user.settings as TestSettings)['stringVal']).toBe(
        'env_string_value',
      );
      expect(
        (settings.user.settings as TestSettings)['undefinedVal'],
      ).toBeUndefined();

      const nestedObj = (settings.user.settings as TestSettings)[
        'nestedObj'
      ] as Record<string, unknown>;
      expect(nestedObj['nestedNull']).toBeNull();
      expect(nestedObj['nestedBool']).toBe(true);
      expect(nestedObj['nestedNum']).toBe(0);
      expect(nestedObj['nestedString']).toBe('literal');
      expect(nestedObj['anotherEnv']).toBe('env_string_nested_value');

      delete process.env['MY_ENV_STRING'];
      delete process.env['MY_ENV_STRING_NESTED'];
    });

    it('should resolve multiple concatenated environment variables in a single string value', () => {
      process.env['TEST_HOST'] = 'myhost';
      process.env['TEST_PORT'] = '9090';
      const userSettingsContent: TestSettings = {
        serverAddress: '${TEST_HOST}:${TEST_PORT}/api',
      };
      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect((settings.user.settings as TestSettings)['serverAddress']).toBe(
        'myhost:9090/api',
      );

      delete process.env['TEST_HOST'];
      delete process.env['TEST_PORT'];
    });

    describe('when GEMINI_CLI_SYSTEM_SETTINGS_PATH is set', () => {
      const MOCK_ENV_SYSTEM_SETTINGS_PATH = path.resolve(
        '/mock/env/system/settings.json',
      );

      beforeEach(() => {
        process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH'] =
          MOCK_ENV_SYSTEM_SETTINGS_PATH;
      });

      afterEach(() => {
        delete process.env['GEMINI_CLI_SYSTEM_SETTINGS_PATH'];
      });

      it('should load system settings from the path specified in the environment variable', () => {
        (mockFsExistsSync as Mock).mockImplementation(
          (p: fs.PathLike) => p === MOCK_ENV_SYSTEM_SETTINGS_PATH,
        );
        const systemSettingsContent = {
          ui: { theme: 'env-var-theme' },
          tools: { sandbox: true },
        };
        (fs.readFileSync as Mock).mockImplementation(
          (p: fs.PathOrFileDescriptor) => {
            if (p === MOCK_ENV_SYSTEM_SETTINGS_PATH)
              return JSON.stringify(systemSettingsContent);
            return '{}';
          },
        );

        const settings = loadSettings(MOCK_WORKSPACE_DIR);

        expect(fs.readFileSync).toHaveBeenCalledWith(
          MOCK_ENV_SYSTEM_SETTINGS_PATH,
          'utf-8',
        );
        expect(settings.system.path).toBe(MOCK_ENV_SYSTEM_SETTINGS_PATH);
        expect(settings.system.settings).toEqual(systemSettingsContent);
        expect(settings.merged).toMatchObject({
          ...systemSettingsContent,
        });
      });
    });

    it('should correctly skip workspace-level loading if workspaceDir is a symlink to home', () => {
      const mockHomeDir = path.resolve('/mock/home/user');
      const mockSymlinkDir = path.resolve('/mock/symlink/to/home');
      const mockWorkspaceSettingsPath = path.join(
        mockSymlinkDir,
        GEMINI_DIR,
        'settings.json',
      );

      vi.mocked(osActual.homedir).mockReturnValue(mockHomeDir);
      vi.mocked(fs.realpathSync).mockImplementation((p: fs.PathLike) => {
        const pStr = p.toString();
        const resolved = path.resolve(pStr);
        if (
          resolved === path.resolve(mockSymlinkDir) ||
          resolved === path.resolve(mockHomeDir)
        ) {
          return mockHomeDir;
        }
        return pStr;
      });

      // Force the storage check to return true for this specific test
      const isWorkspaceHomeDirSpy = vi
        .spyOn(Storage.prototype, 'isWorkspaceHomeDir')
        .mockReturnValue(true);

      (mockFsExistsSync as Mock).mockImplementation(
        (p: string) =>
          // Only return true for workspace settings path to see if it gets loaded
          p === mockWorkspaceSettingsPath,
      );

      try {
        const settings = loadSettings(mockSymlinkDir);

        // Verify that even though the file exists, it was NOT loaded because realpath matched home
        expect(fs.readFileSync).not.toHaveBeenCalledWith(
          mockWorkspaceSettingsPath,
          'utf-8',
        );
        expect(settings.workspace.settings).toEqual({});
      } finally {
        isWorkspaceHomeDirSpy.mockRestore();
      }
    });

    describe('caching', () => {
      it('should cache loadSettings results', () => {
        const mockedRead = vi.mocked(fs.readFileSync);
        mockedRead.mockClear();
        mockedRead.mockReturnValue('{}');
        (mockFsExistsSync as Mock).mockReturnValue(true);

        const settings1 = loadSettings(MOCK_WORKSPACE_DIR);
        const settings2 = loadSettings(MOCK_WORKSPACE_DIR);

        expect(mockedRead).toHaveBeenCalledTimes(5); // system, systemDefaults, user, workspace, and potentially an env file
        expect(settings1).toBe(settings2);
      });

      it('should use separate cache for different workspace directories', () => {
        const mockedRead = vi.mocked(fs.readFileSync);
        mockedRead.mockClear();
        mockedRead.mockReturnValue('{}');
        (mockFsExistsSync as Mock).mockReturnValue(true);

        const workspace1 = path.resolve('/mock/workspace1');
        const workspace2 = path.resolve('/mock/workspace2');

        const settings1 = loadSettings(workspace1);
        const settings2 = loadSettings(workspace2);

        expect(mockedRead).toHaveBeenCalledTimes(10); // 5 for each workspace
        expect(settings1).not.toBe(settings2);
      });

      it('should clear cache when saveSettings is called for user settings', () => {
        const mockedRead = vi.mocked(fs.readFileSync);
        mockedRead.mockClear();
        mockedRead.mockReturnValue('{}');
        (mockFsExistsSync as Mock).mockReturnValue(true);

        const settings1 = loadSettings(MOCK_WORKSPACE_DIR);
        expect(mockedRead).toHaveBeenCalledTimes(5);

        saveSettings(settings1.user);

        const settings2 = loadSettings(MOCK_WORKSPACE_DIR);
        expect(mockedRead).toHaveBeenCalledTimes(10); // Should have re-read from disk
        expect(settings1).not.toBe(settings2);
      });

      it('should clear all caches when saveSettings is called for workspace settings', () => {
        const mockedRead = vi.mocked(fs.readFileSync);
        mockedRead.mockClear();
        mockedRead.mockReturnValue('{}');
        (mockFsExistsSync as Mock).mockReturnValue(true);

        const workspace1 = path.resolve('/mock/workspace1');
        const workspace2 = path.resolve('/mock/workspace2');

        const settings1W1 = loadSettings(workspace1);
        const settings1W2 = loadSettings(workspace2);

        expect(mockedRead).toHaveBeenCalledTimes(10);

        // Save settings for workspace 1
        saveSettings(settings1W1.workspace);

        const settings2W1 = loadSettings(workspace1);
        const settings2W2 = loadSettings(workspace2);

        // Both workspace caches should have been cleared and re-read from disk (+10 reads)
        expect(mockedRead).toHaveBeenCalledTimes(20);
        expect(settings1W1).not.toBe(settings2W1);
        expect(settings1W2).not.toBe(settings2W2);
      });
    });
  });

  describe('excludedProjectEnvVars integration', () => {
    const originalEnv = { ...process.env };

    beforeEach(() => {
      process.env = { ...originalEnv };
    });

    afterEach(() => {
      process.env = originalEnv;
    });

    it('should exclude DEBUG and DEBUG_MODE from project .env files by default', () => {
      // Create a workspace settings file with excludedProjectEnvVars
      const workspaceSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['DEBUG', 'DEBUG_MODE'] },
      };

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH),
      );

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      // Mock findEnvFile to return a project .env file
      const originalFindEnvFile = (
        loadSettings as unknown as { findEnvFile: () => string }
      ).findEnvFile;
      (loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
        () => path.resolve('/mock/project/.env');

      // Mock fs.readFileSync for .env file content
      const originalReadFileSync = fs.readFileSync;
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (p === path.resolve('/mock/project/.env')) {
            return 'DEBUG=true\nDEBUG_MODE=1\nGEMINI_API_KEY=test-key';
          }
          if (
            normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH)
          ) {
            return JSON.stringify(workspaceSettingsContent);
          }
          return '{}';
        },
      );

      try {
        // This will call loadEnvironment internally with the merged settings
        const settings = loadSettings(MOCK_WORKSPACE_DIR);

        // Verify the settings were loaded correctly
        expect(settings.merged.advanced?.excludedEnvVars).toEqual([
          'DEBUG',
          'DEBUG_MODE',
        ]);

        // Note: We can't directly test process.env changes here because the mocking
        // prevents the actual file system operations, but we can verify the settings
        // are correctly merged and passed to loadEnvironment
      } finally {
        (loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
          originalFindEnvFile;
        (fs.readFileSync as Mock).mockImplementation(originalReadFileSync);
      }
    });

    it('should respect custom excludedProjectEnvVars from user settings', () => {
      const userSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['NODE_ENV', 'DEBUG'] },
      };

      (mockFsExistsSync as Mock).mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.user.settings.advanced?.excludedEnvVars).toEqual([
        'NODE_ENV',
        'DEBUG',
      ]);
      expect(settings.merged.advanced?.excludedEnvVars).toEqual([
        'DEBUG',
        'DEBUG_MODE',
        'NODE_ENV',
      ]);
    });

    it('should merge excludedProjectEnvVars with workspace taking precedence', () => {
      const userSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'] },
      };
      const workspaceSettingsContent = {
        general: {},
        advanced: { excludedEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'] },
      };

      (mockFsExistsSync as Mock).mockReturnValue(true);

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.user.settings.advanced?.excludedEnvVars).toEqual([
        'DEBUG',
        'NODE_ENV',
        'USER_VAR',
      ]);
      expect(settings.workspace.settings.advanced?.excludedEnvVars).toEqual([
        'WORKSPACE_DEBUG',
        'WORKSPACE_VAR',
      ]);
      expect(settings.merged.advanced?.excludedEnvVars).toEqual([
        'DEBUG',
        'DEBUG_MODE',
        'NODE_ENV',
        'USER_VAR',
        'WORKSPACE_DEBUG',
        'WORKSPACE_VAR',
      ]);
    });
  });

  describe('with workspace trust', () => {
    it('should merge workspace settings when workspace is trusted', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        ui: { theme: 'dark' },
        tools: { sandbox: false },
      };
      const workspaceSettingsContent = {
        tools: { sandbox: true },
        context: { fileName: 'WORKSPACE.md' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);
      expect(settings.merged.tools?.sandbox).toBe(true);
      expect(settings.merged.context?.fileName).toBe('WORKSPACE.md');
      expect(settings.merged.ui?.theme).toBe('dark');
    });

    it('should NOT merge workspace settings when workspace is not trusted', () => {
      vi.spyOn(trustedFolders, 'isWorkspaceTrusted').mockReturnValue({
        isTrusted: false,
        source: 'file',
      });
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        ui: { theme: 'dark' },
        tools: { sandbox: false },
        context: { fileName: 'USER.md' },
      };
      const workspaceSettingsContent = {
        tools: { sandbox: true },
        context: { fileName: 'WORKSPACE.md' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.tools?.sandbox).toBe(false); // User setting
      expect(settings.merged.context?.fileName).toBe('USER.md'); // User setting
      expect(settings.merged.ui?.theme).toBe('dark'); // User setting
    });

    it('should NOT merge workspace settings when workspace trust is undefined', () => {
      vi.spyOn(trustedFolders, 'isWorkspaceTrusted').mockReturnValue({
        isTrusted: undefined,
        source: undefined,
      });
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const userSettingsContent = {
        ui: { theme: 'dark' },
        tools: { sandbox: false },
        context: { fileName: 'USER.md' },
      };
      const workspaceSettingsContent = {
        tools: { sandbox: true },
        context: { fileName: 'WORKSPACE.md' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      expect(settings.merged.tools?.sandbox).toBe(false); // User setting
      expect(settings.merged.context?.fileName).toBe('USER.md'); // User setting
    });
  });

  describe('loadEnvironment', () => {
    function setup({
      isFolderTrustEnabled = true,
      isWorkspaceTrustedValue = true as boolean | undefined,
    }) {
      delete process.env['GEMINI_API_KEY']; // reset
      delete process.env['TESTTEST']; // reset
      const geminiEnvPath = path.resolve(
        path.join(MOCK_WORKSPACE_DIR, GEMINI_DIR, '.env'),
      );
      const workspaceEnvPath = path.resolve(
        path.join(MOCK_WORKSPACE_DIR, '.env'),
      );

      vi.spyOn(trustedFolders, 'isWorkspaceTrusted').mockReturnValue({
        isTrusted: isWorkspaceTrustedValue,
        source: 'file',
      });
      (mockFsExistsSync as Mock).mockImplementation((p: fs.PathLike) => {
        const normalizedP = path.resolve(p.toString());
        return [
          path.resolve(USER_SETTINGS_PATH),
          geminiEnvPath,
          workspaceEnvPath,
        ].includes(normalizedP);
      });
      const userSettingsContent: Settings = {
        ui: {
          theme: 'dark',
        },
        security: {
          folderTrust: {
            enabled: isFolderTrustEnabled,
          },
        },
        context: {
          fileName: 'USER_CONTEXT.md',
        },
      };
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          const normalizedP = path.resolve(p.toString());
          if (normalizedP === path.resolve(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizedP === geminiEnvPath || normalizedP === workspaceEnvPath)
            return 'TESTTEST=1234\nGEMINI_API_KEY=test-key';
          return '{}';
        },
      );
    }

    it('sets environment variables from .env files', () => {
      setup({ isFolderTrustEnabled: false, isWorkspaceTrustedValue: true });
      const settings = {
        security: { folderTrust: { enabled: false } },
      } as Settings;
      loadEnvironment(settings, MOCK_WORKSPACE_DIR, isWorkspaceTrusted);

      expect(process.env['TESTTEST']).toEqual('1234');
      expect(process.env['GEMINI_API_KEY']).toEqual('test-key');
    });

    it('does not load env files from untrusted spaces when sandboxed', () => {
      setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
      const settings = {
        security: { folderTrust: { enabled: true } },
        tools: { sandbox: true },
      } as Settings;
      loadEnvironment(settings, MOCK_WORKSPACE_DIR, isWorkspaceTrusted);

      expect(process.env['TESTTEST']).not.toEqual('1234');
    });

    it('does NOT load non-whitelisted env files from untrusted spaces even when NOT sandboxed', () => {
      setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
      const settings = {
        security: { folderTrust: { enabled: true } },
        tools: { sandbox: false },
      } as Settings;
      loadEnvironment(settings, MOCK_WORKSPACE_DIR, isWorkspaceTrusted);

      expect(process.env['TESTTEST']).not.toEqual('1234');
      expect(process.env['GEMINI_API_KEY']).toEqual('test-key');
    });

    it('does not load env files when trust is undefined and sandboxed', () => {
      delete process.env['TESTTEST'];
      // isWorkspaceTrusted returns {isTrusted: undefined} for matched rules with no trust value, or no matching rules.
      setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: undefined });
      const settings = {
        security: { folderTrust: { enabled: true } },
        tools: { sandbox: true },
      } as Settings;

      const mockTrustFn = vi.fn().mockReturnValue({ isTrusted: undefined });
      loadEnvironment(settings, MOCK_WORKSPACE_DIR, mockTrustFn);

      expect(process.env['TESTTEST']).not.toEqual('1234');
      expect(process.env['GEMINI_API_KEY']).toEqual('test-key');
    });

    it('loads whitelisted env files from untrusted spaces if sandboxing is enabled', () => {
      setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
      const settings = createTestMergedSettings({
        tools: { sandbox: true },
      });
      loadEnvironment(settings, MOCK_WORKSPACE_DIR, isWorkspaceTrusted);

      // GEMINI_API_KEY is in the whitelist, so it should be loaded.
      expect(process.env['GEMINI_API_KEY']).toEqual('test-key');
      // TESTTEST is NOT in the whitelist, so it should be blocked.
      expect(process.env['TESTTEST']).not.toEqual('1234');
    });

    it('loads whitelisted env files from untrusted spaces if sandboxing is enabled via CLI flag', () => {
      const originalArgv = [...process.argv];
      process.argv.push('-s');
      try {
        setup({ isFolderTrustEnabled: true, isWorkspaceTrustedValue: false });
        const settings = createTestMergedSettings({
          tools: { sandbox: false },
        });
        loadEnvironment(settings, MOCK_WORKSPACE_DIR, isWorkspaceTrusted);

        expect(process.env['GEMINI_API_KEY']).toEqual('test-key');
        expect(process.env['TESTTEST']).not.toEqual('1234');
      } finally {
        process.argv = originalArgv;
      }
    });
  });

  describe('migrateDeprecatedSettings', () => {
    let mockFsExistsSync: Mock;
    let mockFsReadFileSync: Mock;

    beforeEach(() => {
      vi.resetAllMocks();
      mockFsExistsSync = vi.mocked(fs.existsSync);
      mockFsExistsSync.mockReturnValue(true);
      mockFsReadFileSync = vi.mocked(fs.readFileSync);
      mockFsReadFileSync.mockReturnValue('{}');
      vi.spyOn(trustedFolders, 'isWorkspaceTrusted').mockReturnValue({
        isTrusted: true,
        source: undefined,
      });
    });

    afterEach(() => {
      vi.restoreAllMocks();
    });

    it('should not do anything if there are no deprecated settings', () => {
      const userSettingsContent = {
        extensions: {
          enabled: ['user-ext-1'],
        },
      };
      const workspaceSettingsContent = {
        someOtherSetting: 'value',
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          if (normalizePath(p) === normalizePath(MOCK_WORKSPACE_SETTINGS_PATH))
            return JSON.stringify(workspaceSettingsContent);
          return '{}';
        },
      );

      const setValueSpy = vi.spyOn(LoadedSettings.prototype, 'setValue');
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
      setValueSpy.mockClear();

      migrateDeprecatedSettings(loadedSettings, true);

      expect(setValueSpy).not.toHaveBeenCalled();
    });

    it('should migrate general.disableAutoUpdate to general.enableAutoUpdate with inverted value', () => {
      const userSettingsContent = {
        general: {
          disableAutoUpdate: true,
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const setValueSpy = vi.spyOn(LoadedSettings.prototype, 'setValue');
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);

      migrateDeprecatedSettings(loadedSettings, true);

      // Should set new value to false (inverted from true)
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'general',
        expect.objectContaining({ enableAutoUpdate: false }),
      );
    });

    it('should migrate tools.approvalMode to general.defaultApprovalMode', () => {
      const userSettingsContent = {
        tools: {
          approvalMode: 'plan',
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const setValueSpy = vi.spyOn(LoadedSettings.prototype, 'setValue');
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);

      migrateDeprecatedSettings(loadedSettings, true);

      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'general',
        expect.objectContaining({ defaultApprovalMode: 'plan' }),
      );

      // Verify removal
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'tools',
        expect.not.objectContaining({ approvalMode: 'plan' }),
      );
    });

    it('should migrate all 4 inverted boolean settings', () => {
      const userSettingsContent = {
        general: {
          disableAutoUpdate: false,
          disableUpdateNag: true,
        },
        context: {
          fileFiltering: {
            disableFuzzySearch: false,
          },
        },
        ui: {
          accessibility: {
            disableLoadingPhrases: true,
          },
        },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const setValueSpy = vi.spyOn(LoadedSettings.prototype, 'setValue');
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);

      migrateDeprecatedSettings(loadedSettings, true);

      // Check that general settings were migrated with inverted values
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'general',
        expect.objectContaining({ enableAutoUpdate: true }),
      );
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'general',
        expect.objectContaining({ enableAutoUpdateNotification: false }),
      );

      // Check context.fileFiltering was migrated
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'context',
        expect.objectContaining({
          fileFiltering: expect.objectContaining({ enableFuzzySearch: true }),
        }),
      );

      // Check ui.accessibility was migrated
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'ui',
        expect.objectContaining({
          accessibility: expect.objectContaining({
            enableLoadingPhrases: false,
          }),
        }),
      );

      // Check that enableLoadingPhrases: false was further migrated to loadingPhrases: 'off'
      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'ui',
        expect.objectContaining({
          loadingPhrases: 'off',
        }),
      );
    });

    it('should migrate enableLoadingPhrases: false to loadingPhrases: off', () => {
      const userSettingsContent = {
        ui: {
          accessibility: {
            enableLoadingPhrases: false,
          },
        },
      };

      const loadedSettings = createMockSettings(userSettingsContent);
      const setValueSpy = vi.spyOn(loadedSettings, 'setValue');

      migrateDeprecatedSettings(loadedSettings);

      expect(setValueSpy).toHaveBeenCalledWith(
        SettingScope.User,
        'ui',
        expect.objectContaining({
          loadingPhrases: 'off',
        }),
      );
    });

    it('should not migrate enableLoadingPhrases: true to loadingPhrases', () => {
      const userSettingsContent = {
        ui: {
          accessibility: {
            enableLoadingPhrases: true,
          },
        },
      };

      const loadedSettings = createMockSettings(userSettingsContent);
      const setValueSpy = vi.spyOn(loadedSettings, 'setValue');

      migrateDeprecatedSettings(loadedSettings);

      // Should not set loadingPhrases when enableLoadingPhrases is true
      const uiCalls = setValueSpy.mock.calls.filter((call) => call[1] === 'ui');
      for (const call of uiCalls) {
        const uiValue = call[2] as Record<string, unknown>;
        expect(uiValue).not.toHaveProperty('loadingPhrases');
      }
    });

    it('should not overwrite existing loadingPhrases during migration', () => {
      const userSettingsContent = {
        ui: {
          loadingPhrases: 'witty',
          accessibility: {
            enableLoadingPhrases: false,
          },
        },
      };

      const loadedSettings = createMockSettings(userSettingsContent);
      const setValueSpy = vi.spyOn(loadedSettings, 'setValue');

      migrateDeprecatedSettings(loadedSettings);

      // Should not overwrite existing loadingPhrases
      const uiCalls = setValueSpy.mock.calls.filter((call) => call[1] === 'ui');
      for (const call of uiCalls) {
        const uiValue = call[2] as Record<string, unknown>;
        if (uiValue['loadingPhrases'] !== undefined) {
          expect(uiValue['loadingPhrases']).toBe('witty');
        }
      }
    });

    it('should remove deprecated settings by default and prioritize new ones', () => {
      const userSettingsContent = {
        general: {
          disableAutoUpdate: true,
          enableAutoUpdate: true, // Trust this (true) over disableAutoUpdate (true -> false)
        },
        context: {
          fileFiltering: {
            disableFuzzySearch: false,
            enableFuzzySearch: false, // Trust this (false) over disableFuzzySearch (false -> true)
          },
        },
      };

      const loadedSettings = createMockSettings(userSettingsContent);
      const setValueSpy = vi.spyOn(loadedSettings, 'setValue');

      // Default is now removeDeprecated = true
      migrateDeprecatedSettings(loadedSettings);

      // Should remove disableAutoUpdate and trust enableAutoUpdate: true
      expect(setValueSpy).toHaveBeenCalledWith(SettingScope.User, 'general', {
        enableAutoUpdate: true,
      });

      // Should remove disableFuzzySearch and trust enableFuzzySearch: false
      expect(setValueSpy).toHaveBeenCalledWith(SettingScope.User, 'context', {
        fileFiltering: { enableFuzzySearch: false },
      });
    });

    it('should preserve deprecated settings when removeDeprecated is explicitly false', () => {
      const userSettingsContent = {
        general: {
          disableAutoUpdate: true,
          enableAutoUpdate: true,
        },
        context: {
          fileFiltering: {
            disableFuzzySearch: false,
            enableFuzzySearch: false,
          },
        },
      };

      const loadedSettings = createMockSettings(userSettingsContent);

      migrateDeprecatedSettings(loadedSettings, false);

      // Should still have old settings since removeDeprecated = false
      expect(
        loadedSettings.forScope(SettingScope.User).settings.general,
      ).toHaveProperty('disableAutoUpdate');
      expect(
        (
          loadedSettings.forScope(SettingScope.User).settings.context as {
            fileFiltering: { disableFuzzySearch: boolean };
          }
        ).fileFiltering,
      ).toHaveProperty('disableFuzzySearch');
    });

    it('should trigger migration automatically during loadSettings', () => {
      mockFsExistsSync.mockImplementation(
        (p: fs.PathLike) =>
          normalizePath(p) === normalizePath(USER_SETTINGS_PATH),
      );
      const userSettingsContent = {
        general: {
          disableAutoUpdate: true,
        },
      };
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      // Verify it was migrated in the merged settings
      expect(settings.merged.general?.enableAutoUpdate).toBe(false);

      // Verify it was saved back to disk (via setValue calling updateSettingsFilePreservingFormat)
      expect(updateSettingsFilePreservingFormat).toHaveBeenCalledWith(
        USER_SETTINGS_PATH,
        expect.objectContaining({
          general: expect.objectContaining({ enableAutoUpdate: false }),
        }),
      );
    });

    it('should migrate disableUpdateNag to enableAutoUpdateNotification in memory but not save for system and system defaults settings', () => {
      const systemSettingsContent = {
        general: {
          disableUpdateNag: true,
        },
      };
      const systemDefaultsContent = {
        general: {
          disableUpdateNag: false,
        },
      };

      vi.mocked(fs.existsSync).mockReturnValue(true);
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          if (normalizePath(p) === normalizePath(getSystemDefaultsPath())) {
            return JSON.stringify(systemDefaultsContent);
          }
          return '{}';
        },
      );

      const feedbackSpy = mockCoreEvents.emitFeedback;
      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      // Verify system settings were migrated in memory
      expect(settings.system.settings.general).toHaveProperty(
        'enableAutoUpdateNotification',
      );
      expect(
        (settings.system.settings.general as Record<string, unknown>)[
          'enableAutoUpdateNotification'
        ],
      ).toBe(false);

      // Verify system defaults settings were migrated in memory
      expect(settings.systemDefaults.settings.general).toHaveProperty(
        'enableAutoUpdateNotification',
      );
      expect(
        (settings.systemDefaults.settings.general as Record<string, unknown>)[
          'enableAutoUpdateNotification'
        ],
      ).toBe(true);

      // Merged should also reflect it (system overrides defaults, but both are migrated)
      expect(settings.merged.general?.enableAutoUpdateNotification).toBe(false);

      // Verify it was NOT saved back to disk
      expect(updateSettingsFilePreservingFormat).not.toHaveBeenCalledWith(
        getSystemSettingsPath(),
        expect.anything(),
      );
      expect(updateSettingsFilePreservingFormat).not.toHaveBeenCalledWith(
        getSystemDefaultsPath(),
        expect.anything(),
      );

      // Verify warnings were shown
      expect(feedbackSpy).toHaveBeenCalledWith(
        'warning',
        expect.stringContaining(
          'The system configuration contains deprecated settings',
        ),
      );
      expect(feedbackSpy).toHaveBeenCalledWith(
        'warning',
        expect.stringContaining(
          'The system default configuration contains deprecated settings',
        ),
      );
    });

    it('should migrate experimental agent settings in system scope in memory but not save', () => {
      const systemSettingsContent = {
        experimental: {
          codebaseInvestigatorSettings: {
            enabled: true,
          },
        },
      };

      vi.mocked(fs.existsSync).mockReturnValue(true);
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          return '{}';
        },
      );

      const feedbackSpy = mockCoreEvents.emitFeedback;
      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      // Verify it was migrated in memory
      expect(settings.system.settings.agents?.overrides).toMatchObject({
        codebase_investigator: {
          enabled: true,
        },
      });

      // Verify it was NOT saved back to disk
      expect(updateSettingsFilePreservingFormat).not.toHaveBeenCalledWith(
        getSystemSettingsPath(),
        expect.anything(),
      );

      // Verify warnings were shown
      expect(feedbackSpy).toHaveBeenCalledWith(
        'warning',
        expect.stringContaining(
          'The system configuration contains deprecated settings: [experimental.codebaseInvestigatorSettings]',
        ),
      );
    });

    it('should migrate experimental agent settings to agents overrides', () => {
      const userSettingsContent = {
        experimental: {
          codebaseInvestigatorSettings: {
            enabled: true,
            maxNumTurns: 15,
            maxTimeMinutes: 5,
            thinkingBudget: 16384,
            model: 'gemini-1.5-pro',
          },
          cliHelpAgentSettings: {
            enabled: false,
          },
        },
      };

      vi.mocked(fs.existsSync).mockReturnValue(true);
      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(USER_SETTINGS_PATH))
            return JSON.stringify(userSettingsContent);
          return '{}';
        },
      );

      const settings = loadSettings(MOCK_WORKSPACE_DIR);

      // Verify migration to agents.overrides
      expect(settings.user.settings.agents?.overrides).toMatchObject({
        codebase_investigator: {
          enabled: true,
          runConfig: {
            maxTurns: 15,
            maxTimeMinutes: 5,
          },
          modelConfig: {
            model: 'gemini-1.5-pro',
            generateContentConfig: {
              thinkingConfig: {
                thinkingBudget: 16384,
              },
            },
          },
        },
        cli_help: {
          enabled: false,
        },
      });
    });
  });

  describe('saveSettings', () => {
    it('should save settings using updateSettingsFilePreservingFormat', () => {
      const mockUpdateSettings = vi.mocked(updateSettingsFilePreservingFormat);
      const settingsFile = createMockSettings({ ui: { theme: 'dark' } }).user;
      settingsFile.path = path.resolve('/mock/settings.json');

      saveSettings(settingsFile);

      expect(mockUpdateSettings).toHaveBeenCalledWith(
        path.resolve('/mock/settings.json'),
        {
          ui: { theme: 'dark' },
        },
      );
    });

    it('should create directory if it does not exist', () => {
      const mockFsExistsSync = vi.mocked(fs.existsSync);
      const mockFsMkdirSync = vi.mocked(fs.mkdirSync);
      mockFsExistsSync.mockReturnValue(false);

      const settingsFile = createMockSettings({}).user;
      settingsFile.path = path.resolve('/mock/new/dir/settings.json');

      saveSettings(settingsFile);

      expect(mockFsExistsSync).toHaveBeenCalledWith(
        path.resolve('/mock/new/dir'),
      );
      expect(mockFsMkdirSync).toHaveBeenCalledWith(
        path.resolve('/mock/new/dir'),
        {
          recursive: true,
        },
      );
    });

    it('should emit error feedback if saving fails', () => {
      const mockUpdateSettings = vi.mocked(updateSettingsFilePreservingFormat);
      const error = new Error('Write failed');
      mockUpdateSettings.mockImplementation(() => {
        throw error;
      });

      const settingsFile = createMockSettings({}).user;
      settingsFile.path = path.resolve('/mock/settings.json');

      saveSettings(settingsFile);

      expect(mockCoreEvents.emitFeedback).toHaveBeenCalledWith(
        'error',
        'Failed to save settings: Write failed',
        error,
      );
    });
  });

  describe('LoadedSettings and remote admin settings', () => {
    it('should prioritize remote admin settings over file-based admin settings', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        admin: {
          // These should be ignored
          secureModeEnabled: true,
          mcp: { enabled: false },
          extensions: { enabled: false },
        },
        // A non-admin setting to ensure it's still processed
        ui: { theme: 'system-theme' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          return '{}';
        },
      );

      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);

      // 1. Verify that on initial load, file-based admin settings are ignored
      //    and schema defaults are used instead.
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false); // default: false
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true); // default: true
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true); // default: true
      expect(loadedSettings.merged.ui?.theme).toBe('system-theme'); // non-admin setting should be loaded

      // 2. Now, set remote admin settings.
      loadedSettings.setRemoteAdminSettings({
        strictModeDisabled: false,
        mcpSetting: { mcpEnabled: false, mcpConfig: {} },
        cliFeatureSetting: {
          extensionsSetting: { extensionsEnabled: false },
          unmanagedCapabilitiesEnabled: false,
        },
      });

      // 3. Verify that remote admin settings take precedence.
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false);
      // non-admin setting should remain unchanged
      expect(loadedSettings.merged.ui?.theme).toBe('system-theme');
    });

    it('should set remote admin settings and recompute merged settings', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        admin: {
          secureModeEnabled: false,
          mcp: { enabled: false },
          extensions: { enabled: false },
        },
        ui: { theme: 'initial-theme' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          return '{}';
        },
      );

      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
      // Ensure initial state from defaults (as file-based admin settings are ignored)
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
      expect(loadedSettings.merged.ui?.theme).toBe('initial-theme');

      const newRemoteSettings = {
        strictModeDisabled: false,
        mcpSetting: { mcpEnabled: false, mcpConfig: {} },
        cliFeatureSetting: {
          extensionsSetting: { extensionsEnabled: false },
          unmanagedCapabilitiesEnabled: false,
        },
      };

      loadedSettings.setRemoteAdminSettings(newRemoteSettings);

      // Verify that remote admin settings are applied
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(true);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(false);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(false);
      // Non-admin settings should remain untouched
      expect(loadedSettings.merged.ui?.theme).toBe('initial-theme');
    });

    it('should correctly handle undefined remote admin settings', () => {
      (mockFsExistsSync as Mock).mockReturnValue(true);
      const systemSettingsContent = {
        ui: { theme: 'initial-theme' },
      };

      (fs.readFileSync as Mock).mockImplementation(
        (p: fs.PathOrFileDescriptor) => {
          if (normalizePath(p) === normalizePath(getSystemSettingsPath())) {
            return JSON.stringify(systemSettingsContent);
          }
          return '{}';
        },
      );

      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
      // Should have default admin settings
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);

      loadedSettings.setRemoteAdminSettings({}); // Set empty remote settings

      // Admin settings should revert to defaults because there are no remote overrides
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
    });

    it('should un-nest MCP configuration from remote settings', () => {
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
      const mcpServers: Record<string, MCPServerConfig> = {
        'admin-server': {
          url: 'http://admin-mcp.com',
          type: 'sse',
          trust: true,
        },
      };

      loadedSettings.setRemoteAdminSettings({
        mcpSetting: {
          mcpEnabled: true,
          mcpConfig: {
            mcpServers,
          },
        },
      });

      expect(loadedSettings.merged.admin?.mcp?.config).toEqual(mcpServers);
    });

    it('should map requiredMcpConfig from remote settings', () => {
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
      const requiredMcpConfig = {
        'corp-tool': {
          url: 'https://mcp.corp/tool',
          type: 'http' as const,
          trust: true,
        },
      };

      loadedSettings.setRemoteAdminSettings({
        mcpSetting: {
          mcpEnabled: true,
          requiredMcpConfig,
        },
      });

      expect(loadedSettings.merged.admin?.mcp?.requiredConfig).toEqual(
        requiredMcpConfig,
      );
    });

    it('should set skills based on unmanagedCapabilitiesEnabled', () => {
      const loadedSettings = loadSettings();
      loadedSettings.setRemoteAdminSettings({
        cliFeatureSetting: {
          unmanagedCapabilitiesEnabled: true,
        },
      });
      expect(loadedSettings.merged.admin.skills?.enabled).toBe(true);

      loadedSettings.setRemoteAdminSettings({
        cliFeatureSetting: {
          unmanagedCapabilitiesEnabled: false,
        },
      });
      expect(loadedSettings.merged.admin.skills?.enabled).toBe(false);
    });

    it('should handle completely empty remote admin settings response', () => {
      const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);

      loadedSettings.setRemoteAdminSettings({});

      // Should default to schema defaults (standard defaults)
      expect(loadedSettings.merged.admin?.secureModeEnabled).toBe(false);
      expect(loadedSettings.merged.admin?.mcp?.enabled).toBe(true);
      expect(loadedSettings.merged.admin?.extensions?.enabled).toBe(true);
    });
  });

  describe('getDefaultsFromSchema', () => {
    it('should extract defaults from a schema', () => {
      const mockSchema = {
        prop1: {
          type: 'string',
          default: 'default1',
          label: 'Prop 1',
          category: 'General',
          requiresRestart: false,
        },
        nested: {
          type: 'object',
          label: 'Nested',
          category: 'General',
          requiresRestart: false,
          default: {},
          properties: {
            prop2: {
              type: 'number',
              default: 42,
              label: 'Prop 2',
              category: 'General',
              requiresRestart: false,
            },
          },
        },
      };

      const defaults = getDefaultsFromSchema(mockSchema as SettingsSchema);
      expect(defaults).toEqual({
        prop1: 'default1',
        nested: {
          prop2: 42,
        },
      });
    });
  });

  describe('Reactivity & Snapshots', () => {
    let loadedSettings: LoadedSettings;

    beforeEach(() => {
      const emptySettingsFile: SettingsFile = {
        path: path.resolve('/mock/path'),
        settings: {},
        originalSettings: {},
      };

      loadedSettings = new LoadedSettings(
        { ...emptySettingsFile, path: getSystemSettingsPath() },
        { ...emptySettingsFile, path: getSystemDefaultsPath() },
        { ...emptySettingsFile, path: USER_SETTINGS_PATH },
        { ...emptySettingsFile, path: MOCK_WORKSPACE_SETTINGS_PATH },
        true, // isTrusted
        [],
      );
    });

    it('getSnapshot() should return stable reference if no changes occur', () => {
      const snap1 = loadedSettings.getSnapshot();
      const snap2 = loadedSettings.getSnapshot();
      expect(snap1).toBe(snap2);
    });

    it('getSnapshot() should preserve readOnly metadata for each scope', () => {
      const readonlySettings = new LoadedSettings(
        {
          path: getSystemSettingsPath(),
          settings: {},
          originalSettings: {},
          readOnly: true,
        },
        {
          path: getSystemDefaultsPath(),
          settings: {},
          originalSettings: {},
          readOnly: true,
        },
        {
          path: USER_SETTINGS_PATH,
          settings: {},
          originalSettings: {},
          readOnly: false,
        },
        {
          path: MOCK_WORKSPACE_SETTINGS_PATH,
          settings: {},
          originalSettings: {},
          readOnly: true,
        },
        true,
        [],
      );

      const snapshot = readonlySettings.getSnapshot();

      expect(snapshot.system.readOnly).toBe(true);
      expect(snapshot.systemDefaults.readOnly).toBe(true);
      expect(snapshot.user.readOnly).toBe(false);
      expect(snapshot.workspace.readOnly).toBe(true);
    });

    it('setValue() should create a new snapshot reference and emit event', () => {
      const oldSnapshot = loadedSettings.getSnapshot();
      const oldUserRef = oldSnapshot.user.settings;

      loadedSettings.setValue(SettingScope.User, 'ui.theme', 'high-contrast');

      const newSnapshot = loadedSettings.getSnapshot();

      expect(newSnapshot).not.toBe(oldSnapshot);
      expect(newSnapshot.user.settings).not.toBe(oldUserRef);
      expect(newSnapshot.user.settings.ui?.theme).toBe('high-contrast');

      expect(newSnapshot.system.settings).not.toBe(oldSnapshot.system.settings);

      expect(mockCoreEvents.emitSettingsChanged).toHaveBeenCalled();
    });
  });

  describe('Security and Sandbox', () => {
    let originalArgv: string[];
    let originalEnv: NodeJS.ProcessEnv;

    beforeEach(() => {
      originalArgv = [...process.argv];
      originalEnv = { ...process.env };
      // Clear relevant env vars
      delete process.env['GEMINI_API_KEY'];
      delete process.env['GOOGLE_API_KEY'];
      delete process.env['GOOGLE_CLOUD_PROJECT'];
      delete process.env['GOOGLE_CLOUD_LOCATION'];
      delete process.env['CLOUD_SHELL'];
      delete process.env['MALICIOUS_VAR'];
      delete process.env['FOO'];
      delete process.env['_GEMINI_USER_GCP_PROJECT'];
      vi.resetAllMocks();
      vi.mocked(fs.existsSync).mockReturnValue(false);
    });

    afterEach(() => {
      process.argv = originalArgv;
      process.env = originalEnv;
    });

    describe('sandbox detection', () => {
      it('should detect sandbox when -s is a real flag', () => {
        process.argv = ['node', 'gemini', '-s', 'some prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(true);
        vi.mocked(fs.readFileSync).mockReturnValue(
          'FOO=bar\nGEMINI_API_KEY=secret',
        );

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        // If sandboxed and untrusted, FOO should NOT be loaded, but GEMINI_API_KEY should be.
        expect(process.env['FOO']).toBeUndefined();
        expect(process.env['GEMINI_API_KEY']).toBe('secret');
      });

      it('should detect sandbox when --sandbox is a real flag', () => {
        process.argv = ['node', 'gemini', '--sandbox', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(true);
        vi.mocked(fs.readFileSync).mockReturnValue('GEMINI_API_KEY=secret');

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toBe('secret');
      });

      it('should ignore sandbox flags if they appear after --', () => {
        process.argv = ['node', 'gemini', '--', '-s', 'some prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );
        vi.mocked(fs.readFileSync).mockReturnValue('GEMINI_API_KEY=secret');

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toEqual('secret');
      });

      it('should NOT be tricked by positional arguments that look like flags', () => {
        process.argv = ['node', 'gemini', 'my -s prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );
        vi.mocked(fs.readFileSync).mockReturnValue('GEMINI_API_KEY=secret');

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toEqual('secret');
      });
    });

    describe('env var sanitization', () => {
      it('should strictly enforce whitelist in untrusted/sandboxed mode', () => {
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );
        vi.mocked(fs.readFileSync).mockReturnValue(`
GEMINI_API_KEY=secret-key
MALICIOUS_VAR=should-be-ignored
GOOGLE_API_KEY=another-secret
    `);

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toBe('secret-key');
        expect(process.env['GOOGLE_API_KEY']).toBe('another-secret');
        expect(process.env['MALICIOUS_VAR']).toBeUndefined();
      });

      it('should sanitize shell injection characters in whitelisted env vars in untrusted mode', () => {
        process.argv = ['node', 'gemini', '--sandbox', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );

        const maliciousPayload = 'key-$(whoami)-`id`-&|;><*?[]{}';
        vi.mocked(fs.readFileSync).mockReturnValue(
          `GEMINI_API_KEY=${maliciousPayload}`,
        );

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        // sanitizeEnvVar: value.replace(/[^a-zA-Z0-9\-_./]/g, '')
        expect(process.env['GEMINI_API_KEY']).toBe('key-whoami-id-');
      });

      it('should allow . and / in whitelisted env vars but sanitize other characters in untrusted mode', () => {
        process.argv = ['node', 'gemini', '--sandbox', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );

        const complexPayload = 'secret-123/path.to/somewhere;rm -rf /';
        vi.mocked(fs.readFileSync).mockReturnValue(
          `GEMINI_API_KEY=${complexPayload}`,
        );

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toBe(
          'secret-123/path.to/somewhererm-rf/',
        );
      });

      it('should NOT sanitize variables from trusted sources', () => {
        process.argv = ['node', 'gemini', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: true,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(true);

        vi.mocked(fs.readFileSync).mockReturnValue('FOO=$(bar)');

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        // Trusted source, no sanitization
        expect(process.env['FOO']).toBe('$(bar)');
      });

      it('should load environment variables normally when workspace is TRUSTED even if "sandboxed"', () => {
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: true,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockImplementation((path) =>
          path.toString().endsWith('.env'),
        );
        vi.mocked(fs.readFileSync).mockReturnValue(`
GEMINI_API_KEY=un-sanitized;key!
MALICIOUS_VAR=allowed-because-trusted
    `);

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GEMINI_API_KEY']).toBe('un-sanitized;key!');
        expect(process.env['MALICIOUS_VAR']).toBe('allowed-because-trusted');
      });

      it('should sanitize value in sanitizeEnvVar helper', () => {
        expect(sanitizeEnvVar('$(calc)')).toBe('calc');
        expect(sanitizeEnvVar('`rm -rf /`')).toBe('rm-rf/');
        expect(sanitizeEnvVar('normal-project-123')).toBe('normal-project-123');
        expect(sanitizeEnvVar('us-central1')).toBe('us-central1');
      });
    });

    describe('Cloud Shell security', () => {
      it('should handle Cloud Shell special defaults securely when untrusted', () => {
        process.env['CLOUD_SHELL'] = 'true';
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });

        // No .env file
        vi.mocked(fs.existsSync).mockReturnValue(false);

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('cloudshell-gca');
      });

      it('should not override GOOGLE_CLOUD_PROJECT in Cloud Shell when auth type is vertex-ai', () => {
        vi.stubEnv('CLOUD_SHELL', 'true');
        vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'my-vertex-project');
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });

        // No .env file
        vi.mocked(fs.existsSync).mockReturnValue(false);

        loadEnvironment(
          createMockSettings({
            tools: { sandbox: false },
            security: { auth: { selectedType: AuthType.USE_VERTEX_AI } },
          }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('my-vertex-project');
      });

      it('should respect .env override for GOOGLE_CLOUD_PROJECT in Cloud Shell when auth type is vertex-ai', () => {
        vi.stubEnv('CLOUD_SHELL', 'true');
        vi.stubEnv('GOOGLE_CLOUD_PROJECT', 'my-vertex-project');
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: true,
          source: 'file',
        });

        // Mock .env file to override the shell project
        vi.mocked(fs.existsSync).mockReturnValue(true);
        vi.mocked(fs.readFileSync).mockReturnValue(
          'GOOGLE_CLOUD_PROJECT=env-vertex-project',
        );

        loadEnvironment(
          createMockSettings({
            tools: { sandbox: false },
            security: { auth: { selectedType: AuthType.USE_VERTEX_AI } },
          }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('env-vertex-project');
      });

      it('should clear cloudshell-gca when switching to Vertex AI without an original project', () => {
        process.env['CLOUD_SHELL'] = 'true';
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(false);

        // First call: normal Cloud Shell auth sets cloudshell-gca
        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );
        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('cloudshell-gca');

        // Second call: user switched to Vertex AI, should remove cloudshell-gca
        loadEnvironment(
          createMockSettings({
            tools: { sandbox: false },
            security: { auth: { selectedType: AuthType.USE_VERTEX_AI } },
          }).merged,
          MOCK_WORKSPACE_DIR,
        );
        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBeUndefined();
      });

      it('should restore original project when switching to Vertex AI after Cloud Shell override', () => {
        process.env['CLOUD_SHELL'] = 'true';
        process.env['GOOGLE_CLOUD_PROJECT'] = 'my-real-project';
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(false);

        // First call: saves original to _GEMINI_USER_GCP_PROJECT, sets cloudshell-gca
        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );
        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('cloudshell-gca');
        expect(process.env['_GEMINI_USER_GCP_PROJECT']).toBe('my-real-project');

        // Second call: switching to Vertex AI should restore the saved value
        loadEnvironment(
          createMockSettings({
            tools: { sandbox: false },
            security: { auth: { selectedType: AuthType.USE_VERTEX_AI } },
          }).merged,
          MOCK_WORKSPACE_DIR,
        );
        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('my-real-project');
      });

      it('should restore project after restart when child inherits cloudshell-gca', () => {
        // Simulate child process after restart: inherits cloudshell-gca and
        // the saved original from the parent process.
        process.env['CLOUD_SHELL'] = 'true';
        process.env['GOOGLE_CLOUD_PROJECT'] = 'cloudshell-gca';
        process.env['_GEMINI_USER_GCP_PROJECT'] = 'my-real-project';
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(false);

        loadEnvironment(
          createMockSettings({
            tools: { sandbox: false },
            security: { auth: { selectedType: AuthType.USE_VERTEX_AI } },
          }).merged,
          MOCK_WORKSPACE_DIR,
        );
        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe('my-real-project');
      });

      it('should sanitize GOOGLE_CLOUD_PROJECT in Cloud Shell when loaded from .env in untrusted mode', () => {
        process.env['CLOUD_SHELL'] = 'true';
        process.argv = ['node', 'gemini', '-s', 'prompt'];
        vi.mocked(isWorkspaceTrusted).mockReturnValue({
          isTrusted: false,
          source: 'file',
        });
        vi.mocked(fs.existsSync).mockReturnValue(true);
        vi.mocked(fs.readFileSync).mockReturnValue(
          'GOOGLE_CLOUD_PROJECT=attacker-project;inject',
        );

        loadEnvironment(
          createMockSettings({ tools: { sandbox: false } }).merged,
          MOCK_WORKSPACE_DIR,
        );

        expect(process.env['GOOGLE_CLOUD_PROJECT']).toBe(
          'attacker-projectinject',
        );
      });
    });
  });
});

describe('LoadedSettings Isolation and Serializability', () => {
  let loadedSettings: LoadedSettings;

  interface TestData {
    a: {
      b: number;
    };
  }

  beforeEach(() => {
    vi.resetAllMocks();

    // Create a minimal LoadedSettings instance
    const emptyScope = {
      path: path.resolve('/mock/settings.json'),
      settings: {},
      originalSettings: {},
    } as unknown as SettingsFile;

    loadedSettings = new LoadedSettings(
      emptyScope, // system
      emptyScope, // systemDefaults
      { ...emptyScope }, // user
      emptyScope, // workspace
      true, // isTrusted
    );
  });

  describe('setValue Isolation', () => {
    it('should isolate state between settings and originalSettings', () => {
      const complexValue: TestData = { a: { b: 1 } };
      loadedSettings.setValue(SettingScope.User, 'test', complexValue);

      const userSettings = loadedSettings.forScope(SettingScope.User);
      const settingsValue = (userSettings.settings as Record<string, unknown>)[
        'test'
      ] as TestData;
      const originalValue = (
        userSettings.originalSettings as Record<string, unknown>
      )['test'] as TestData;

      // Verify they are equal but different references
      expect(settingsValue).toEqual(complexValue);
      expect(originalValue).toEqual(complexValue);
      expect(settingsValue).not.toBe(complexValue);
      expect(originalValue).not.toBe(complexValue);
      expect(settingsValue).not.toBe(originalValue);

      // Modify the in-memory setting object
      settingsValue.a.b = 2;

      // originalSettings should NOT be affected
      expect(originalValue.a.b).toBe(1);
    });

    it('should not share references between settings and originalSettings (original servers test)', () => {
      const mcpServers = {
        'test-server': { command: 'echo' },
      };

      loadedSettings.setValue(SettingScope.User, 'mcpServers', mcpServers);

      // Modify the original object
      delete (mcpServers as Record<string, unknown>)['test-server'];

      // The settings in LoadedSettings should still have the server
      const userSettings = loadedSettings.forScope(SettingScope.User);
      expect(
        (userSettings.settings.mcpServers as Record<string, unknown>)[
          'test-server'
        ],
      ).toBeDefined();
      expect(
        (userSettings.originalSettings.mcpServers as Record<string, unknown>)[
          'test-server'
        ],
      ).toBeDefined();

      // They should also be different objects from each other
      expect(userSettings.settings.mcpServers).not.toBe(
        userSettings.originalSettings.mcpServers,
      );
    });
  });

  describe('setValue Serializability', () => {
    it('should preserve Map/Set types (via structuredClone)', () => {
      const mapValue = { myMap: new Map([['key', 'value']]) };
      loadedSettings.setValue(SettingScope.User, 'test', mapValue);

      const userSettings = loadedSettings.forScope(SettingScope.User);
      const settingsValue = (userSettings.settings as Record<string, unknown>)[
        'test'
      ] as { myMap: Map<string, string> };

      // Map is preserved by structuredClone
      expect(settingsValue.myMap).toBeInstanceOf(Map);
      expect(settingsValue.myMap.get('key')).toBe('value');

      // But it should be a different reference
      expect(settingsValue.myMap).not.toBe(mapValue.myMap);
    });

    it('should handle circular references (structuredClone supports them, but deepMerge may not)', () => {
      const circular: Record<string, unknown> = { a: 1 };
      circular['self'] = circular;

      // structuredClone(circular) works, but LoadedSettings.setValue calls
      // computeMergedSettings() -> customDeepMerge() which blows up on circularity.
      expect(() => {
        loadedSettings.setValue(SettingScope.User, 'test', circular);
      }).toThrow(/Maximum call stack size exceeded/);
    });
  });
});
