/**
 * Contract test for IConfigLoader interface
 *
 * This test verifies that any implementation of IConfigLoader
 * adheres to the contract defined in the specifications.
 *
 * 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 from contract (this import will fail until types are implemented)
import type {
  IConfigLoader,
  LoadResult,
  Account,
  Platform,
  ConfigFile
} from '@/specs/001-credential-manager/contracts/credential-manager';

describe('IConfigLoader Contract Tests', () => {
  let configLoader: IConfigLoader;
  let tempDir: string;
  let testConfigPath: string;

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

    // Create temporary directory for test files
    tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'config-loader-test-'));
    testConfigPath = path.join(tempDir, 'test-config.json');
  });

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

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

  describe('Configuration Loading', () => {
    test('should load valid JSON configuration file', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "test-pacifica-account",
            platform: Platform.PACIFICA,
            name: "Test Pacifica Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(testConfigPath, JSON.stringify(configData, null, 2));

      // Act
      const result: LoadResult = await configLoader.loadConfig(testConfigPath);

      // Assert
      expect(result).toBeDefined();
      expect(result.success).toBe(true);
      expect(Array.isArray(result.accounts)).toBe(true);
      expect(result.accounts).toHaveLength(1);
      expect(typeof result.loadTime).toBe('number');
      expect(result.loadTime).toBeGreaterThan(0);

      // Performance requirement: load time < 100ms
      expect(result.loadTime).toBeLessThan(100);

      // Validate loaded account
      const account = result.accounts[0];
      expect(account.id).toBe("test-pacifica-account");
      expect(account.platform).toBe(Platform.PACIFICA);
      expect(account.name).toBe("Test Pacifica Account");
      expect(account.credentials).toBeDefined();
    });

    test('should load valid YAML configuration file', async () => {
      // Arrange
      const yamlConfigPath = path.join(tempDir, 'test-config.yaml');
      const yamlContent = `
version: "1.0"
accounts:
  - id: "test-aster-account"
    platform: "ASTER"
    name: "Test Aster Account"
    credentials:
      type: "eip191"
      privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
`;

      await fs.writeFile(yamlConfigPath, yamlContent);

      // Act
      const result: LoadResult = await configLoader.loadConfig(yamlConfigPath);

      // Assert
      expect(result.success).toBe(true);
      expect(result.accounts).toHaveLength(1);
      expect(result.accounts[0].id).toBe("test-aster-account");
      expect(result.accounts[0].platform).toBe(Platform.ASTER);
    });

    test('should handle missing configuration file gracefully', async () => {
      // Arrange
      const nonExistentPath = path.join(tempDir, 'nonexistent-config.json');

      // Act
      const result: LoadResult = await configLoader.loadConfig(nonExistentPath);

      // Assert
      expect(result.success).toBe(false);
      expect(result.accounts).toHaveLength(0);
      expect(result.errors).toBeDefined();
      expect(result.errors!.length).toBeGreaterThan(0);
      expect(typeof result.loadTime).toBe('number');
    });

    test('should handle malformed JSON configuration file', async () => {
      // Arrange
      const malformedContent = '{ "version": "1.0", "accounts": [ invalid json }';
      await fs.writeFile(testConfigPath, malformedContent);

      // Act
      const result: LoadResult = await configLoader.loadConfig(testConfigPath);

      // Assert
      expect(result.success).toBe(false);
      expect(result.accounts).toHaveLength(0);
      expect(result.errors).toBeDefined();
      expect(result.errors!.length).toBeGreaterThan(0);
      expect(result.errors![0]).toContain('JSON');
    });

    test('should validate configuration schema', async () => {
      // Arrange - missing required fields
      const invalidConfig = {
        version: "1.0",
        accounts: [
          {
            // Missing id and platform
            name: "Invalid Account"
          }
        ]
      };

      await fs.writeFile(testConfigPath, JSON.stringify(invalidConfig));

      // Act
      const result: LoadResult = await configLoader.loadConfig(testConfigPath);

      // Assert
      expect(result.success).toBe(false);
      expect(result.errors).toBeDefined();
      expect(result.errors!.some(error => error.includes('id') || error.includes('platform'))).toBe(true);
    });

    test('should handle empty configuration file', async () => {
      // Arrange
      await fs.writeFile(testConfigPath, '');

      // Act
      const result: LoadResult = await configLoader.loadConfig(testConfigPath);

      // Assert
      expect(result.success).toBe(false);
      expect(result.errors).toBeDefined();
    });

    test('should load multiple accounts from configuration', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "pacifica-account-1",
            platform: Platform.PACIFICA,
            name: "Pacifica Account 1",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          },
          {
            id: "binance-account-1",
            platform: Platform.BINANCE,
            name: "Binance Account 1",
            credentials: {
              type: "hmac",
              apiKey: "test-api-key",
              secretKey: "test-secret-key"
            }
          }
        ]
      };

      await fs.writeFile(testConfigPath, JSON.stringify(configData, null, 2));

      // Act
      const result: LoadResult = await configLoader.loadConfig(testConfigPath);

      // Assert
      expect(result.success).toBe(true);
      expect(result.accounts).toHaveLength(2);
      expect(result.accounts[0].platform).toBe(Platform.PACIFICA);
      expect(result.accounts[1].platform).toBe(Platform.BINANCE);
    });
  });

  describe('Configuration Watching', () => {
    test('should start watching configuration file changes', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: []
      };
      await fs.writeFile(testConfigPath, JSON.stringify(configData));

      const mockCallback = jest.fn();

      // Act & Assert - should not throw
      expect(() => {
        configLoader.watchConfig(testConfigPath, mockCallback);
      }).not.toThrow();

      // Verify callback function signature
      expect(typeof mockCallback).toBe('function');
    });

    test('should call callback when configuration file changes', async () => {
      // Arrange
      const initialConfig: ConfigFile = {
        version: "1.0",
        accounts: []
      };
      await fs.writeFile(testConfigPath, JSON.stringify(initialConfig));

      const callbackPromise = new Promise<Account[]>((resolve) => {
        const mockCallback = (accounts: Account[]) => {
          resolve(accounts);
        };
        configLoader.watchConfig(testConfigPath, mockCallback);
      });

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

      // Act - modify the configuration file
      const updatedConfig: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "new-account",
            platform: Platform.PACIFICA,
            name: "New Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };
      await fs.writeFile(testConfigPath, JSON.stringify(updatedConfig, null, 2));

      // Assert - wait for callback with timeout
      const accounts = await Promise.race([
        callbackPromise,
        new Promise<Account[]>((_, reject) =>
          setTimeout(() => reject(new Error('Callback timeout')), 5000)
        )
      ]);

      expect(accounts).toHaveLength(1);
      expect(accounts[0].id).toBe("new-account");
    });

    test('should stop watching configuration file changes', () => {
      // Act & Assert - should not throw
      expect(() => {
        configLoader.stopWatching();
      }).not.toThrow();
    });

    test('should handle watching non-existent file', () => {
      // Arrange
      const nonExistentPath = path.join(tempDir, 'nonexistent.json');
      const mockCallback = jest.fn();

      // Act & Assert - should not throw, may log warning
      expect(() => {
        configLoader.watchConfig(nonExistentPath, mockCallback);
      }).not.toThrow();
    });

    test('should handle multiple file watchers', async () => {
      // Arrange
      const config1Path = path.join(tempDir, 'config1.json');
      const config2Path = path.join(tempDir, 'config2.json');

      const configData: ConfigFile = {
        version: "1.0",
        accounts: []
      };

      await fs.writeFile(config1Path, JSON.stringify(configData));
      await fs.writeFile(config2Path, JSON.stringify(configData));

      const callback1 = jest.fn();
      const callback2 = jest.fn();

      // Act & Assert - should handle multiple watchers
      expect(() => {
        configLoader.watchConfig(config1Path, callback1);
        configLoader.watchConfig(config2Path, callback2);
      }).not.toThrow();
    });

    test('should debounce rapid file changes', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: []
      };
      await fs.writeFile(testConfigPath, JSON.stringify(configData));

      const mockCallback = jest.fn();
      configLoader.watchConfig(testConfigPath, mockCallback);

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

      // Act - make rapid changes
      for (let i = 0; i < 5; i++) {
        const updatedConfig = {
          ...configData,
          accounts: [{ id: `account-${i}`, platform: Platform.PACIFICA, name: `Account ${i}`, credentials: { type: "ed25519", privateKey: "test" } }]
        };
        await fs.writeFile(testConfigPath, JSON.stringify(updatedConfig));
        await new Promise(resolve => setTimeout(resolve, 10)); // Small delay between writes
      }

      // Wait for debouncing
      await new Promise(resolve => setTimeout(resolve, 1000));

      // Assert - should be called fewer times than the number of writes due to debouncing
      expect(mockCallback.mock.calls.length).toBeLessThan(5);
      expect(mockCallback.mock.calls.length).toBeGreaterThan(0);
    });
  });

  describe('Error Handling', () => {
    test('should handle invalid file paths', async () => {
      const invalidPaths = ['', '   ', null as any, undefined as any];

      for (const invalidPath of invalidPaths) {
        const result = await configLoader.loadConfig(invalidPath);
        expect(result.success).toBe(false);
        expect(result.errors).toBeDefined();
      }
    });

    test('should handle permission denied errors', async () => {
      // This test may not work on all systems, but should not crash
      const restrictedPath = '/root/restricted-config.json';

      const result = await configLoader.loadConfig(restrictedPath);

      // Should either succeed (if accessible) or fail gracefully
      expect(typeof result.success).toBe('boolean');
      if (!result.success) {
        expect(result.errors).toBeDefined();
      }
    });

    test('should handle large configuration files', async () => {
      // Arrange - create a large config with many accounts
      const largeConfig: ConfigFile = {
        version: "1.0",
        accounts: Array(1000).fill(null).map((_, index) => ({
          id: `account-${index}`,
          platform: Platform.PACIFICA,
          name: `Account ${index}`,
          credentials: {
            type: "ed25519",
            privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
          }
        }))
      };

      await fs.writeFile(testConfigPath, JSON.stringify(largeConfig));

      // Act
      const result = await configLoader.loadConfig(testConfigPath);

      // Assert - should handle large files gracefully
      expect(typeof result.success).toBe('boolean');
      if (result.success) {
        expect(result.accounts).toHaveLength(1000);
        // Should still meet performance requirements even with large files
        expect(result.loadTime).toBeLessThan(1000); // 1 second max for very large files
      }
    });
  });

  describe('Performance Requirements', () => {
    test('should meet hot reload performance requirements', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: Array(50).fill(null).map((_, index) => ({
          id: `account-${index}`,
          platform: Platform.PACIFICA,
          name: `Account ${index}`,
          credentials: {
            type: "ed25519",
            privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
          }
        }))
      };

      await fs.writeFile(testConfigPath, JSON.stringify(configData));

      // Act - measure reload time
      const startTime = Date.now();
      const result = await configLoader.loadConfig(testConfigPath);
      const loadTime = Date.now() - startTime;

      // Assert - hot reload performance requirement: < 100ms
      expect(result.success).toBe(true);
      expect(loadTime).toBeLessThan(100);
      expect(result.loadTime).toBeLessThan(100);
    });

    test('should handle concurrent load requests', async () => {
      // Arrange
      const configData: ConfigFile = {
        version: "1.0",
        accounts: [
          {
            id: "test-account",
            platform: Platform.PACIFICA,
            name: "Test Account",
            credentials: {
              type: "ed25519",
              privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
            }
          }
        ]
      };

      await fs.writeFile(testConfigPath, JSON.stringify(configData));

      // Act - make concurrent load requests
      const loadPromises = Array(10).fill(null).map(() =>
        configLoader.loadConfig(testConfigPath)
      );

      const results = await Promise.all(loadPromises);

      // Assert - all should succeed
      results.forEach(result => {
        expect(result.success).toBe(true);
        expect(result.accounts).toHaveLength(1);
      });
    });
  });
});