/**
 * Tests for Persistent OpenChrome Profile architecture (Issue #74)
 *
 * Covers:
 * 1. ProfileManager.getOrCreatePersistentProfile() — directory creation
 * 2. ProfileManager.needsSync() — freshness-based sync decision
 * 3. ProfileManager.syncProfileData() — atomic SQLite backup + fallback
 * 4. ProfileManager.getSyncMetadata() / updateSyncMetadata() — metadata persistence
 * 5. ProfileManager.resolveProfile() — profile selection priority
 */

import { execFileSync } from 'child_process';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';

// Override the global mock from tests/setup.ts
jest.unmock('../../src/chrome/profile-manager');

import { ProfileManager } from '../../src/chrome/profile-manager';
import type { SyncMetadata, ChromeProfileInfo } from '../../src/chrome/profile-manager';

jest.mock('child_process', () => {
  const actual = jest.requireActual('child_process');
  return {
    ...actual,
    execFileSync: jest.fn(),
  };
});

const mockExecFileSync = execFileSync as jest.MockedFunction<typeof execFileSync>;

describe('ProfileManager', () => {
  let consoleErrorSpy: jest.SpyInstance;
  let tmpDir: string;

  beforeEach(() => {
    consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
    mockExecFileSync.mockReset();
    tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-pm-test-'));

    // Override static constants to use temp directories for test isolation
    Object.defineProperty(ProfileManager, 'PERSISTENT_PROFILE_DIR', {
      value: path.join(tmpDir, 'persistent-profile'),
      configurable: true,
    });
    Object.defineProperty(ProfileManager, 'SYNC_METADATA_PATH', {
      value: path.join(tmpDir, 'sync-metadata.json'),
      configurable: true,
    });
  });

  afterEach(() => {
    consoleErrorSpy.mockRestore();
    fs.rmSync(tmpDir, { recursive: true, force: true });
  });

  // =========================================================================
  // getOrCreatePersistentProfile()
  // =========================================================================

  describe('getOrCreatePersistentProfile()', () => {
    it('should create persistent profile directory if it does not exist', () => {
      const manager = new ProfileManager();
      const result = manager.getOrCreatePersistentProfile();

      expect(result).toBe(ProfileManager.PERSISTENT_PROFILE_DIR);
      expect(fs.existsSync(result)).toBe(true);
      expect(fs.existsSync(path.join(result, 'Default'))).toBe(true);
    });

    it('should return existing directory without recreating', () => {
      const profileDir = ProfileManager.PERSISTENT_PROFILE_DIR;
      fs.mkdirSync(path.join(profileDir, 'Default'), { recursive: true });

      const manager = new ProfileManager();
      const result = manager.getOrCreatePersistentProfile();

      expect(result).toBe(profileDir);
      expect(fs.existsSync(path.join(result, 'Default'))).toBe(true);
    });

    it('should create Default subdirectory even if parent exists', () => {
      const profileDir = ProfileManager.PERSISTENT_PROFILE_DIR;
      fs.mkdirSync(profileDir, { recursive: true });
      // Default/ doesn't exist yet

      const manager = new ProfileManager();
      manager.getOrCreatePersistentProfile();

      expect(fs.existsSync(path.join(profileDir, 'Default'))).toBe(true);
    });
  });

  // =========================================================================
  // needsSync()
  // =========================================================================

  describe('needsSync()', () => {
    let sourceDir: string;

    beforeEach(() => {
      sourceDir = path.join(tmpDir, 'source-profile');
      fs.mkdirSync(path.join(sourceDir, 'Default'), { recursive: true });
    });

    it('should return true when no sync metadata exists', () => {
      // No metadata file — never synced
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'data');

      const manager = new ProfileManager();
      expect(manager.needsSync(sourceDir)).toBe(true);
    });

    it('should return true when source cookies changed (hash mismatch)', () => {
      // Write metadata with old hash
      const metadata: SyncMetadata = {
        lastSyncTimestamp: Date.now(),
        sourceProfileHash: '999999:100', // Different from actual file
        syncCount: 1,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-data');

      const manager = new ProfileManager();
      expect(manager.needsSync(sourceDir)).toBe(true);
    });

    it('should return true when cookies are stale (> 30 min)', () => {
      // Create source Cookies file first to compute hash
      const cookiesPath = path.join(sourceDir, 'Default', 'Cookies');
      fs.writeFileSync(cookiesPath, 'cookie-data');
      const stat = fs.statSync(cookiesPath);
      const hash = `${stat.mtimeMs}:${stat.size}`;

      // Write metadata with matching hash but old timestamp
      const metadata: SyncMetadata = {
        lastSyncTimestamp: Date.now() - (31 * 60 * 1000), // 31 minutes ago
        sourceProfileHash: hash,
        syncCount: 1,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));

      const manager = new ProfileManager();
      expect(manager.needsSync(sourceDir)).toBe(true);
    });

    it('should return false when cookies are fresh and unchanged', () => {
      // Create source Cookies file
      const cookiesPath = path.join(sourceDir, 'Default', 'Cookies');
      fs.writeFileSync(cookiesPath, 'cookie-data');
      const stat = fs.statSync(cookiesPath);
      const hash = `${stat.mtimeMs}:${stat.size}`;

      // Write metadata with current timestamp and matching hash
      const metadata: SyncMetadata = {
        lastSyncTimestamp: Date.now(),
        sourceProfileHash: hash,
        syncCount: 1,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));

      const manager = new ProfileManager();
      expect(manager.needsSync(sourceDir)).toBe(false);
    });

    it('should return false when source Cookies file does not exist but metadata exists', () => {
      // No Cookies file, but metadata exists → can't sync
      const metadata: SyncMetadata = {
        lastSyncTimestamp: Date.now(),
        sourceProfileHash: 'old',
        syncCount: 1,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));

      const manager = new ProfileManager();
      expect(manager.needsSync(sourceDir)).toBe(false);
    });

    it('should return false when persistent profile Cookies were modified after last sync (headless guard)', () => {
      // Set up source Cookies file and compute its hash
      const cookiesPath = path.join(sourceDir, 'Default', 'Cookies');
      fs.writeFileSync(cookiesPath, 'source-cookie-data');
      const stat = fs.statSync(cookiesPath);
      const hash = `${stat.mtimeMs}:${stat.size}`;

      // Write metadata with an old timestamp (simulating last sync happened in the past)
      const oldTimestamp = Date.now() - (40 * 60 * 1000); // 40 minutes ago
      const metadata: SyncMetadata = {
        lastSyncTimestamp: oldTimestamp,
        sourceProfileHash: hash,
        syncCount: 1,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));

      // Create the persistent profile's Cookies file with a recent mtime
      // (simulating a headless session that wrote cookies after the last sync)
      const persistentCookiesDir = path.join(ProfileManager.PERSISTENT_PROFILE_DIR, 'Default');
      fs.mkdirSync(persistentCookiesDir, { recursive: true });
      fs.writeFileSync(path.join(persistentCookiesDir, 'Cookies'), 'headless-acquired-cookies');
      // The persistent Cookies file was just written — its mtime is newer than oldTimestamp

      const manager = new ProfileManager();
      // Should NOT sync: persistent cookies were written after the last sync by a headless session
      expect(manager.needsSync(sourceDir)).toBe(false);
    });
  });

  // =========================================================================
  // syncProfileData()
  // =========================================================================

  describe('syncProfileData()', () => {
    let sourceDir: string;
    let destDir: string;

    beforeEach(() => {
      sourceDir = path.join(tmpDir, 'source');
      destDir = path.join(tmpDir, 'dest');
      fs.mkdirSync(path.join(sourceDir, 'Default'), { recursive: true });
    });

    it('should use sqlite3 backup when available and return atomic: true', () => {
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-db');

      // Mock: `which sqlite3` succeeds, backup command succeeds
      mockExecFileSync.mockImplementation((file: unknown, args?: unknown) => {
        const fileStr = String(file);
        if (fileStr === 'which' || fileStr === 'where') {
          return Buffer.from('/usr/bin/sqlite3');
        }
        if (fileStr === 'sqlite3') {
          // Simulate creating the backup file
          const destDefault = path.join(destDir, 'Default');
          fs.mkdirSync(destDefault, { recursive: true });
          fs.writeFileSync(path.join(destDefault, 'Cookies'), 'backed-up-db');
          return Buffer.from('');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      const result = manager.syncProfileData(sourceDir, destDir);

      expect(result.atomic).toBe(true);
    });

    it('should skip cookie copy when sqlite3 is not available', () => {
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-db');
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies-wal'), 'wal-data');
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies-shm'), 'shm-data');
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies-journal'), 'journal-data');
      fs.writeFileSync(
        path.join(sourceDir, 'Default', 'Preferences'),
        JSON.stringify({ profile: { exit_type: 'Crashed' } })
      );

      // Mock: `which sqlite3` fails
      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          throw new Error('not found');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      const result = manager.syncProfileData(sourceDir, destDir);

      expect(result.atomic).toBe(false);
      expect(result.success).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-wal'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-shm'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-journal'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Preferences'))).toBe(true);
    });

    it('should clean up WAL/SHM/journal files after atomic backup', () => {
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-db');

      // Pre-create stale WAL/SHM/journal at dest
      fs.mkdirSync(path.join(destDir, 'Default'), { recursive: true });
      fs.writeFileSync(path.join(destDir, 'Default', 'Cookies-wal'), 'stale-wal');
      fs.writeFileSync(path.join(destDir, 'Default', 'Cookies-shm'), 'stale-shm');
      fs.writeFileSync(path.join(destDir, 'Default', 'Cookies-journal'), 'stale-journal');

      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          return Buffer.from('/usr/bin/sqlite3');
        }
        if (String(file) === 'sqlite3') {
          return Buffer.from('');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      manager.syncProfileData(sourceDir, destDir);

      // WAL/SHM/journal should be cleaned up
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-wal'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-shm'))).toBe(false);
      expect(fs.existsSync(path.join(destDir, 'Default', 'Cookies-journal'))).toBe(false);
    });

    it('should copy and patch Preferences', () => {
      const sourcePrefs = {
        profile: { exit_type: 'Crashed', exited_cleanly: false, name: 'Default' },
        session: { startup_urls: ['https://example.com'], restore_on_startup: 1 },
      };
      fs.writeFileSync(
        path.join(sourceDir, 'Default', 'Preferences'),
        JSON.stringify(sourcePrefs)
      );

      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          throw new Error('not found');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      manager.syncProfileData(sourceDir, destDir);

      const destPrefsPath = path.join(destDir, 'Default', 'Preferences');
      expect(fs.existsSync(destPrefsPath)).toBe(true);

      const destPrefs = JSON.parse(fs.readFileSync(destPrefsPath, 'utf8'));
      expect(destPrefs.profile.exit_type).toBe('Normal');
      expect(destPrefs.profile.exited_cleanly).toBe(true);
      expect(destPrefs.profile.name).toBe('Default'); // Other fields preserved
      expect(destPrefs.session.restore_on_startup).toBe(5);
      expect(destPrefs.session.startup_urls).toBeUndefined();
    });

    it('should copy Local State file', () => {
      fs.writeFileSync(path.join(sourceDir, 'Local State'), '{"os_crypt":{"key":"abc"}}');

      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          throw new Error('not found');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      manager.syncProfileData(sourceDir, destDir);

      expect(fs.existsSync(path.join(destDir, 'Local State'))).toBe(true);
      expect(fs.readFileSync(path.join(destDir, 'Local State'), 'utf8')).toBe(
        '{"os_crypt":{"key":"abc"}}'
      );
    });

    it('should handle missing source Cookies file gracefully', () => {
      // No Cookies file in source
      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          throw new Error('not found');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      expect(() => manager.syncProfileData(sourceDir, destDir)).not.toThrow();
    });

    it('should update sync metadata after successful atomic cookie sync', () => {
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-db');

      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          return Buffer.from('/usr/bin/sqlite3');
        }
        if (String(file) === 'sqlite3') {
          fs.mkdirSync(path.join(destDir, 'Default'), { recursive: true });
          fs.writeFileSync(path.join(destDir, 'Default', 'Cookies'), 'backed-up-db');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      manager.syncProfileData(sourceDir, destDir);

      const metadata = manager.getSyncMetadata();
      expect(metadata).not.toBeNull();
      expect(metadata!.syncCount).toBe(1);
      expect(metadata!.sourceProfileDir).toBe(sourceDir);
    });

    it('should not update sync metadata when sqlite3 is unavailable', () => {
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'cookie-db');

      mockExecFileSync.mockImplementation((file: unknown) => {
        if (String(file) === 'which' || String(file) === 'where') {
          throw new Error('not found');
        }
        return Buffer.from('');
      });

      const manager = new ProfileManager();
      manager.syncProfileData(sourceDir, destDir);

      expect(manager.getSyncMetadata()).toBeNull();
    });
  });

  // =========================================================================
  // getSyncMetadata()
  // =========================================================================

  describe('getSyncMetadata()', () => {
    it('should return null when metadata file does not exist', () => {
      const manager = new ProfileManager();
      expect(manager.getSyncMetadata()).toBeNull();
    });

    it('should parse valid metadata JSON', () => {
      const metadata: SyncMetadata = {
        lastSyncTimestamp: 1700000000000,
        sourceProfileHash: '123456:1024',
        syncCount: 5,
        sourceProfileDir: '/some/path',
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(metadata));

      const manager = new ProfileManager();
      const result = manager.getSyncMetadata();

      expect(result).toEqual(metadata);
    });

    it('should return null on corrupted JSON', () => {
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, 'not valid json {{{');

      const manager = new ProfileManager();
      expect(manager.getSyncMetadata()).toBeNull();
    });
  });

  // =========================================================================
  // updateSyncMetadata()
  // =========================================================================

  describe('updateSyncMetadata()', () => {
    let sourceDir: string;

    beforeEach(() => {
      sourceDir = path.join(tmpDir, 'source');
      fs.mkdirSync(path.join(sourceDir, 'Default'), { recursive: true });
      fs.writeFileSync(path.join(sourceDir, 'Default', 'Cookies'), 'data');
    });

    it('should write metadata with a current timestamp', () => {
      const manager = new ProfileManager();
      const before = Date.now();
      manager.updateSyncMetadata(sourceDir);
      const after = Date.now();

      const metadata = manager.getSyncMetadata();
      expect(metadata).not.toBeNull();
      expect(metadata!.lastSyncTimestamp).toBeGreaterThanOrEqual(before);
      expect(metadata!.lastSyncTimestamp).toBeLessThanOrEqual(after);
    });

    it('should increment syncCount from 0 to 1', () => {
      const manager = new ProfileManager();
      manager.updateSyncMetadata(sourceDir);

      const metadata = manager.getSyncMetadata();
      expect(metadata!.syncCount).toBe(1);
    });

    it('should increment syncCount from existing value', () => {
      // Write initial metadata
      const initial: SyncMetadata = {
        lastSyncTimestamp: Date.now() - 1000,
        sourceProfileHash: 'old-hash',
        syncCount: 3,
        sourceProfileDir: sourceDir,
      };
      fs.writeFileSync(ProfileManager.SYNC_METADATA_PATH, JSON.stringify(initial));

      const manager = new ProfileManager();
      manager.updateSyncMetadata(sourceDir);

      const metadata = manager.getSyncMetadata();
      expect(metadata!.syncCount).toBe(4);
    });

    it('should update sourceProfileHash to reflect current Cookies file', () => {
      const cookiesPath = path.join(sourceDir, 'Default', 'Cookies');
      const stat = fs.statSync(cookiesPath);
      const expectedHash = `${stat.mtimeMs}:${stat.size}`;

      const manager = new ProfileManager();
      manager.updateSyncMetadata(sourceDir);

      const metadata = manager.getSyncMetadata();
      expect(metadata!.sourceProfileHash).toBe(expectedHash);
    });
  });

  // =========================================================================
  // resolveProfile()
  // =========================================================================

  describe('resolveProfile()', () => {
    it('should return explicit profile when explicitUserDataDir provided', () => {
      const manager = new ProfileManager();
      const result = manager.resolveProfile({
        realProfileDir: '/some/chrome/profile',
        isProfileLocked: false,
        explicitUserDataDir: '/my/custom/dir',
      });

      expect(result.profileType).toBe('explicit');
      expect(result.userDataDir).toBe('/my/custom/dir');
      expect(result.syncPerformed).toBe(false);
    });

    it('should return temp profile when useTempProfile is true', () => {
      const manager = new ProfileManager();
      const result = manager.resolveProfile({
        realProfileDir: '/some/chrome/profile',
        isProfileLocked: false,
        useTempProfile: true,
      });

      expect(result.profileType).toBe('temp');
      expect(result.userDataDir).toContain('openchrome-');
      expect(result.syncPerformed).toBe(false);
    });

    it('should return stable headless-shell profile when usingHeadlessShell is true', () => {
      const manager = new ProfileManager();
      const result = manager.resolveProfile({
        realProfileDir: '/some/chrome/profile',
        isProfileLocked: false,
        usingHeadlessShell: true,
      });

      expect(result.profileType).toBe('headless-shell');
      expect(result.userDataDir).toContain('headless-shell-profile');
      expect(result.syncPerformed).toBe(false);
    });

    it('should return real profile when realProfileDir provided and not locked', () => {
      const manager = new ProfileManager();
      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
      });

      expect(result.profileType).toBe('real');
      expect(result.userDataDir).toBe('/real/chrome/profile');
      expect(result.syncPerformed).toBe(false);
    });

    it('should return persistent profile with sync when locked and stale', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(true);
      jest.spyOn(manager, 'syncProfileData').mockReturnValue({ atomic: true, success: true });
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.userDataDir).toBe('/mock/persistent');
      expect(result.syncPerformed).toBe(true);
      expect(manager.syncProfileData).toHaveBeenCalledWith('/real/chrome/profile', '/mock/persistent');
    });

    it('should not mark sync performed when cookie sync is not atomic', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(true);
      jest.spyOn(manager, 'syncProfileData').mockReturnValue({ atomic: false, success: false });
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.syncPerformed).toBe(false);
    });

    it('should return persistent profile without sync when locked but fresh', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(false);
      jest.spyOn(manager, 'syncProfileData');
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.userDataDir).toBe('/mock/persistent');
      expect(result.syncPerformed).toBe(false);
      expect(manager.syncProfileData).not.toHaveBeenCalled();
    });

    it('should return persistent profile when no real profile dir exists', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: null,
        isProfileLocked: false,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.userDataDir).toBe('/mock/persistent');
      expect(result.syncPerformed).toBe(false);
    });
  });

  // =========================================================================
  // resolveProfile() — isAutoLaunch (Chrome 136+ compatibility)
  // =========================================================================

  describe('resolveProfile() with isAutoLaunch', () => {
    it('should return persistent profile (not real) when isAutoLaunch is true and profile is unlocked', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(false);
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
        isAutoLaunch: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.userDataDir).toBe('/mock/persistent');
    });

    it('should perform cookie sync when isAutoLaunch is true and cookies are stale', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(true);
      jest.spyOn(manager, 'syncProfileData').mockReturnValue({ atomic: true, success: true });
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
        isAutoLaunch: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.syncPerformed).toBe(true);
      expect(manager.syncProfileData).toHaveBeenCalledWith('/real/chrome/profile', '/mock/persistent');
    });

    it('should return real profile when isAutoLaunch is false and profile is unlocked (backward compat)', () => {
      const manager = new ProfileManager();

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
        isAutoLaunch: false,
      });

      expect(result.profileType).toBe('real');
      expect(result.userDataDir).toBe('/real/chrome/profile');
    });

    it('should return real profile when isAutoLaunch is omitted and profile is unlocked (backward compat)', () => {
      const manager = new ProfileManager();

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
      });

      expect(result.profileType).toBe('real');
      expect(result.userDataDir).toBe('/real/chrome/profile');
    });

    it('should prioritize explicit --user-data-dir over isAutoLaunch', () => {
      const manager = new ProfileManager();

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: false,
        explicitUserDataDir: '/my/custom/dir',
        isAutoLaunch: true,
      });

      expect(result.profileType).toBe('explicit');
      expect(result.userDataDir).toBe('/my/custom/dir');
    });

    it('should return persistent profile when isAutoLaunch is true and profile is also locked', () => {
      const manager = new ProfileManager();
      jest.spyOn(manager, 'needsSync').mockReturnValue(false);
      jest.spyOn(manager, 'getOrCreatePersistentProfile').mockReturnValue('/mock/persistent');

      const result = manager.resolveProfile({
        realProfileDir: '/real/chrome/profile',
        isProfileLocked: true,
        isAutoLaunch: true,
      });

      expect(result.profileType).toBe('persistent');
      expect(result.userDataDir).toBe('/mock/persistent');
    });
  });

  // =========================================================================
  // listProfiles()
  // =========================================================================

  describe('listProfiles', () => {
    it('should parse Local State and return profile info', () => {
      const listTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-profile-test-'));
      const localState = {
        profile: {
          info_cache: {
            'Default': { name: 'Person 1', user_name: 'user@gmail.com' },
            'Profile 1': { name: 'Work', user_name: 'work@company.com' },
            'Profile 2': { name: 'Side Project' },
          },
          last_used: 'Profile 1',
        },
      };
      fs.writeFileSync(path.join(listTmpDir, 'Local State'), JSON.stringify(localState));

      const pm = new ProfileManager();
      const profiles = pm.listProfiles(listTmpDir);

      expect(profiles).toHaveLength(3);
      expect(profiles[0]).toEqual({ directory: 'Default', name: 'Person 1', userName: 'user@gmail.com' });
      expect(profiles[1]).toEqual({ directory: 'Profile 1', name: 'Work', userName: 'work@company.com', isActive: true });
      expect(profiles[2]).toEqual({ directory: 'Profile 2', name: 'Side Project' });

      fs.rmSync(listTmpDir, { recursive: true, force: true });
    });

    it('should return empty array when Local State is missing', () => {
      const pm = new ProfileManager();
      const profiles = pm.listProfiles('/nonexistent/path');
      expect(profiles).toEqual([]);
    });

    it('should return empty array when profile info_cache is missing', () => {
      const listTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-profile-test-'));
      fs.writeFileSync(path.join(listTmpDir, 'Local State'), JSON.stringify({ other: 'data' }));

      const pm = new ProfileManager();
      const profiles = pm.listProfiles(listTmpDir);
      expect(profiles).toEqual([]);

      fs.rmSync(listTmpDir, { recursive: true, force: true });
    });
  });

  // =========================================================================
  // cleanStaleLocks()
  // =========================================================================

  describe('cleanStaleLocks()', () => {
    let profileDir: string;

    beforeEach(() => {
      profileDir = path.join(tmpDir, 'stale-profile');
      fs.mkdirSync(path.join(profileDir, 'Default'), { recursive: true });
    });

    it('should remove SingletonLock, SingletonSocket, SingletonCookie files', () => {
      // Create the lock files
      fs.writeFileSync(path.join(profileDir, 'SingletonLock'), '');
      fs.writeFileSync(path.join(profileDir, 'SingletonSocket'), '');
      fs.writeFileSync(path.join(profileDir, 'SingletonCookie'), '');

      const manager = new ProfileManager();
      manager.cleanStaleLocks(profileDir);

      expect(fs.existsSync(path.join(profileDir, 'SingletonLock'))).toBe(false);
      expect(fs.existsSync(path.join(profileDir, 'SingletonSocket'))).toBe(false);
      expect(fs.existsSync(path.join(profileDir, 'SingletonCookie'))).toBe(false);
    });

    it('should remove lockfile (Windows)', () => {
      fs.writeFileSync(path.join(profileDir, 'lockfile'), '');

      const manager = new ProfileManager();
      manager.cleanStaleLocks(profileDir);

      expect(fs.existsSync(path.join(profileDir, 'lockfile'))).toBe(false);
    });

    it('should handle symlinks (SingletonLock is a symlink on Unix)', () => {
      // SingletonLock on Unix is a symlink pointing to "hostname-pid"
      const symlinkPath = path.join(profileDir, 'SingletonLock');
      fs.symlinkSync('localhost-12345', symlinkPath);

      // Verify the symlink exists via lstat (existsSync would return false for dangling symlinks)
      expect(() => fs.lstatSync(symlinkPath)).not.toThrow();

      const manager = new ProfileManager();
      manager.cleanStaleLocks(profileDir);

      // Symlink should be removed
      expect(() => fs.lstatSync(symlinkPath)).toThrow();
    });

    it('should patch Preferences exit_type to "Normal"', () => {
      const prefs = {
        profile: { exit_type: 'Crashed', exited_cleanly: false, name: 'Default' },
        session: { startup_urls: ['https://example.com'], restore_on_startup: 1 },
      };
      fs.writeFileSync(
        path.join(profileDir, 'Default', 'Preferences'),
        JSON.stringify(prefs)
      );

      const manager = new ProfileManager();
      manager.cleanStaleLocks(profileDir);

      const patched = JSON.parse(
        fs.readFileSync(path.join(profileDir, 'Default', 'Preferences'), 'utf8')
      );
      expect(patched.profile.exit_type).toBe('Normal');
      expect(patched.profile.exited_cleanly).toBe(true);
      expect(patched.profile.name).toBe('Default'); // Other fields preserved
      expect(patched.session.restore_on_startup).toBe(5);
      expect(patched.session.startup_urls).toBeUndefined();
    });

    it('should handle missing Preferences file gracefully', () => {
      // No Preferences file exists
      const manager = new ProfileManager();
      expect(() => manager.cleanStaleLocks(profileDir)).not.toThrow();
    });

    it('should handle corrupt Preferences JSON gracefully', () => {
      fs.writeFileSync(
        path.join(profileDir, 'Default', 'Preferences'),
        'not valid json {{{'
      );

      const manager = new ProfileManager();
      expect(() => manager.cleanStaleLocks(profileDir)).not.toThrow();
    });

    it('should be a no-op when no lock files exist', () => {
      // profileDir exists but has no lock files and no Preferences
      const manager = new ProfileManager();
      expect(() => manager.cleanStaleLocks(profileDir)).not.toThrow();
      // Verify no console.error about removing locks was called
      const removeLockCalls = consoleErrorSpy.mock.calls.filter(
        (call: unknown[]) => String(call[0]).includes('Removed stale lock')
      );
      expect(removeLockCalls).toHaveLength(0);
    });
  });

  // =========================================================================
  // dynamic profile subdirectory
  // =========================================================================

  describe('dynamic profile subdirectory', () => {
    it('needsSync should use custom profileSubdir', () => {
      const pm = new ProfileManager();
      const syncTmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'oc-sync-test-'));
      const profileDir = path.join(syncTmpDir, 'Profile 1');
      fs.mkdirSync(profileDir, { recursive: true });
      fs.writeFileSync(path.join(profileDir, 'Cookies'), 'test');

      // Should look for Cookies in "Profile 1" subdir, not "Default"
      const result = pm.needsSync(syncTmpDir, 'Profile 1');
      expect(result).toBe(true); // true because no prior sync metadata

      fs.rmSync(syncTmpDir, { recursive: true, force: true });
    });

    it('resolveProfile should pass through profileDirectory', () => {
      const pm = new ProfileManager();
      const result = pm.resolveProfile({
        realProfileDir: null,
        isProfileLocked: false,
        profileDirectory: 'Profile 1',
      });
      expect(result.profileDirectory).toBe('Profile 1');
    });
  });
});
