/**
 * Integration test for hot configuration reload
 *
 * This test verifies the complete hot reload workflow:
 * 1. Load initial configuration
 * 2. Start watching for changes
 * 3. Modify configuration file
 * 4. Verify automatic reload within 100ms
 * 5. Verify account updates are reflected
 *
 * Tests MUST FAIL initially until implementation is provided.
 */

import { describe, test, expect, beforeEach, afterEach } from '@jest/globals';
import * as fs from 'fs/promises';
import * as path from 'path';
import * as os from 'os';

// Import types (this import will fail until types are implemented)
import type {
  ICredentialManager,
  Account,
  Platform,
  ConfigFile
} from '@/specs/001-credential-manager/contracts/credential-manager';

describe('Hot Configuration Reload Integration Tests', () => {
  let credentialManager: ICredentialManager;
  let tempDir: string;
  let configPath: string;

  beforeEach(async () => {
    // This will fail until CredentialManager is implemented
    const { CredentialManager } = await import('@/core/credential-manager/CredentialManager');
    credentialManager = new CredentialManager();

    // Create temporary directory for test files
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'hot-reload-test-'));
    configPath = path.join(tempDir, 'credentials.json');
  });

  afterEach(async () => {
    // Clean up
    credentialManager.stopWatching();

    // Remove temporary directory
    try {
      await fs.rm(tempDir, { recursive: true, force: true });
    } catch (error) {
      // Ignore cleanup errors
    }
  });

  describe('Initial Load and Setup', () => {
    test('should load initial configuration successfully', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "initial-pacifica-account",
            platform: Platform.PACIFICA,
            name: "Initial Pacifica Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig, null, 2));

      // Act
      const loadResult = await credentialManager.loadConfig(configPath);

      // Assert
      expect(loadResult.success).toBe(true);
      expect(loadResult.accounts).toHaveLength(1);
      expect(loadResult.accounts[0].id).toBe("initial-pacifica-account");

      // Verify account is accessible via getAccount
      const account = credentialManager.getAccount("initial-pacifica-account");
      expect(account).not.toBeNull();
      expect(account!.platform).toBe(Platform.PACIFICA);
    });

    test('should start file watching without errors', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      const changeCallback = jest.fn();

      // Act & Assert - should not throw
      expect(() => {
        credentialManager.watchConfig(configPath, changeCallback);
      }).not.toThrow();
    });
  });

  describe('Hot Reload Performance', () => {
    test('should complete hot reload within 100ms performance requirement', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "performance-test-account",
            platform: Platform.PACIFICA,
            name: "Performance Test Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig, null, 2));
      await credentialManager.loadConfig(configPath);

      // Setup reload detection
      const reloadPromise = new Promise<{accounts: Account[], reloadTime: number}>((resolve) => {
        const startTime = Date.now();
        credentialManager.watchConfig(configPath, (accounts) => {
          const reloadTime = Date.now() - startTime;
          resolve({ accounts, reloadTime });
        });
      });

      // Wait for watcher to initialize
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - modify configuration
      const modifiedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "performance-test-account",
            platform: Platform.PACIFICA,
            name: "Modified Performance Test Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          },
          {
            id: "new-account",
            platform: Platform.BINANCE,
            name: "New Account",
            credentials: {
              type: "hmac",
              apiKey: "test-api-key",
              secretKey: "test-secret-key"
            }
          }
        ]
      };

      const modifyStartTime = Date.now();
      await fs.writeFile(configPath, JSON.stringify(modifiedConfig, null, 2));

      // Assert - wait for reload with timeout
      const { accounts, reloadTime } = await Promise.race([
        reloadPromise,
        new Promise<never>((_, reject) =>
          setTimeout(() => reject(new Error('Hot reload timeout - exceeded 5 seconds')), 5000)
        )
      ]);

      // Performance requirement: < 100ms
      expect(reloadTime).toBeLessThan(100);
      expect(accounts).toHaveLength(2);

      // Verify accounts are updated in credential manager
      const updatedAccount = credentialManager.getAccount("performance-test-account");
      expect(updatedAccount!.name).toBe("Modified Performance Test Account");

      const newAccount = credentialManager.getAccount("new-account");
      expect(newAccount).not.toBeNull();
      expect(newAccount!.platform).toBe(Platform.BINANCE);
    });

    test('should handle rapid successive file changes efficiently', async () => {
      // Arrange
      const baseConfig: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(configPath, JSON.stringify(baseConfig));
      await credentialManager.loadConfig(configPath);

      let reloadCount = 0;
      const reloadTimes: number[] = [];

      credentialManager.watchConfig(configPath, (accounts) => {
        reloadCount++;
        reloadTimes.push(Date.now());
      });

      // Wait for watcher to initialize
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - make rapid changes
      const startTime = Date.now();
      for (let i = 0; i < 5; i++) {
        const config = {
          ...baseConfig,
          accounts: [
            {
              id: `rapid-account-${i}`,
              platform: Platform.PACIFICA,
              name: `Rapid Account ${i}`,
              credentials: {
                type: "ed25519",
                privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
              }
            }
          ]
        };
        await fs.writeFile(configPath, JSON.stringify(config));
        await new Promise(resolve => setTimeout(resolve, 20)); // Small delay between changes
      }

      // Wait for debouncing and final reload
      await new Promise(resolve => setTimeout(resolve, 500));

      // Assert - should be debounced (fewer reloads than changes)
      expect(reloadCount).toBeLessThan(5);
      expect(reloadCount).toBeGreaterThan(0);

      // Final state should be correct
      const finalAccount = credentialManager.getAccount("rapid-account-4");
      expect(finalAccount).not.toBeNull();
    });
  });

  describe('Account Updates and State Management', () => {
    test('should add new accounts on configuration update', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "existing-account",
            platform: Platform.PACIFICA,
            name: "Existing Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      const updatePromise = new Promise<Account[]>((resolve) => {
        credentialManager.watchConfig(configPath, resolve);
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - add new account
      const updatedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          ...initialConfig.accounts,
          {
            id: "new-aster-account",
            platform: Platform.ASTER,
            name: "New Aster Account",
            credentials: {
              type: "eip191",
              privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2));

      // Assert
      const accounts = await updatePromise;
      expect(accounts).toHaveLength(2);

      // Verify both accounts are accessible
      expect(credentialManager.getAccount("existing-account")).not.toBeNull();
      expect(credentialManager.getAccount("new-aster-account")).not.toBeNull();
      expect(credentialManager.getAccount("new-aster-account")!.platform).toBe(Platform.ASTER);
    });

    test('should remove accounts when deleted from configuration', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "account-to-keep",
            platform: Platform.PACIFICA,
            name: "Account to Keep",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          },
          {
            id: "account-to-remove",
            platform: Platform.BINANCE,
            name: "Account to Remove",
            credentials: {
              type: "hmac",
              apiKey: "test-api-key",
              secretKey: "test-secret-key"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      // Verify both accounts exist initially
      expect(credentialManager.getAccount("account-to-keep")).not.toBeNull();
      expect(credentialManager.getAccount("account-to-remove")).not.toBeNull();

      const updatePromise = new Promise<Account[]>((resolve) => {
        credentialManager.watchConfig(configPath, resolve);
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - remove one account
      const updatedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "account-to-keep",
            platform: Platform.PACIFICA,
            name: "Account to Keep",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2));

      // Assert
      const accounts = await updatePromise;
      expect(accounts).toHaveLength(1);

      // Verify correct account state
      expect(credentialManager.getAccount("account-to-keep")).not.toBeNull();
      expect(credentialManager.getAccount("account-to-remove")).toBeNull();
    });

    test('should update existing account credentials', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "updatable-account",
            platform: Platform.PACIFICA,
            name: "Original Name",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      const updatePromise = new Promise<Account[]>((resolve) => {
        credentialManager.watchConfig(configPath, resolve);
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - update account details
      const updatedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "updatable-account",
            platform: Platform.PACIFICA,
            name: "Updated Name",
            credentials: {
              type: "ed25519",
              privateKey: "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(updatedConfig, null, 2));

      // Assert
      const accounts = await updatePromise;
      expect(accounts).toHaveLength(1);

      const updatedAccount = credentialManager.getAccount("updatable-account");
      expect(updatedAccount).not.toBeNull();
      expect(updatedAccount!.name).toBe("Updated Name");
      expect(updatedAccount!.credentials.privateKey).toBe("fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210");
    });
  });

  describe('Error Handling During Hot Reload', () => {
    test('should handle malformed configuration during reload', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "stable-account",
            platform: Platform.PACIFICA,
            name: "Stable Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      // Verify initial state
      expect(credentialManager.getAccount("stable-account")).not.toBeNull();

      let errorOccurred = false;
      credentialManager.watchConfig(configPath, (accounts) => {
        // This callback should not be called for malformed config
        errorOccurred = true;
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - write malformed configuration
      const malformedConfig = '{ "version": "1.0", "accounts": [ invalid json }';
      await fs.writeFile(configPath, malformedConfig);

      // Wait for file watcher to process
      await new Promise(resolve => setTimeout(resolve, 500));

      // Assert - original state should be preserved
      expect(errorOccurred).toBe(false);
      expect(credentialManager.getAccount("stable-account")).not.toBeNull();
    });

    test('should handle file deletion during watching', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "persistent-account",
            platform: Platform.PACIFICA,
            name: "Persistent Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      credentialManager.watchConfig(configPath, (accounts) => {
        // Should handle file deletion gracefully
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - delete configuration file
      await fs.unlink(configPath);

      // Wait for file watcher to process
      await new Promise(resolve => setTimeout(resolve, 500));

      // Assert - should not crash, previous state may be preserved
      expect(() => {
        credentialManager.getAccount("persistent-account");
      }).not.toThrow();
    });

    test('should handle temporary file operations (atomic writes)', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      let updateCount = 0;
      credentialManager.watchConfig(configPath, (accounts) => {
        updateCount++;
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - simulate atomic write operations (common with editors)
      const tempPath = configPath + '.tmp';
      const finalConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "atomic-account",
            platform: Platform.PACIFICA,
            name: "Atomic Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      // Write to temp file then rename (atomic operation)
      await fs.writeFile(tempPath, JSON.stringify(finalConfig, null, 2));
      await fs.rename(tempPath, configPath);

      // Wait for file watcher to process
      await new Promise(resolve => setTimeout(resolve, 500));

      // Assert - should detect the final state
      expect(updateCount).toBeGreaterThan(0);
      expect(credentialManager.getAccount("atomic-account")).not.toBeNull();
    });
  });

  describe('Cleanup and Resource Management', () => {
    test('should stop watching when requested', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(configPath, JSON.stringify(initialConfig));
      await credentialManager.loadConfig(configPath);

      let callbackCount = 0;
      credentialManager.watchConfig(configPath, () => {
        callbackCount++;
      });

      // Wait for watcher
      await new Promise(resolve => setTimeout(resolve, 100));

      // Act - stop watching
      credentialManager.stopWatching();

      // Modify file after stopping
      const modifiedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "should-not-trigger",
            platform: Platform.PACIFICA,
            name: "Should Not Trigger",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(configPath, JSON.stringify(modifiedConfig));

      // Wait to ensure no callback is triggered
      await new Promise(resolve => setTimeout(resolve, 500));

      // Assert - callback should not have been called after stopping
      expect(callbackCount).toBe(0);
    });

    test('should handle multiple start/stop watch cycles', async () => {
      // Arrange
      const config: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(configPath, JSON.stringify(config));
      await credentialManager.loadConfig(configPath);

      // Act & Assert - multiple cycles should not cause issues
      for (let i = 0; i < 3; i++) {
        credentialManager.watchConfig(configPath, () => {});
        await new Promise(resolve => setTimeout(resolve, 50));
        credentialManager.stopWatching();
        await new Promise(resolve => setTimeout(resolve, 50));
      }

      // Should end in clean state
      expect(() => {
        credentialManager.stopWatching();
      }).not.toThrow();
    });
  });
});