/**
 * Integration Test: IntegrationTestHarness
 * 
 * Tests the IntegrationTestHarness infrastructure for orchestrating integration tests
 * - Validates setup and teardown hooks
 * - Tests database transaction management
 * - Verifies constraint violation detection
 * 
 * Focus: Infrastructure testing, NOT business logic
 */

import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach, vi } from 'vitest';
import { IntegrationTestHarness, createTestHarness, DEFAULT_TEST_CONFIG } from './index';
import { DatabaseManager } from './database-manager';
import { ApiClient } from './api-client';

describe('IntegrationTestHarness - Infrastructure Tests', () => {
  let harness: IntegrationTestHarness;

  beforeAll(() => {
    // Create a test harness with default configuration
    harness = createTestHarness();
  });

  describe('Construction', () => {
    it('should be constructed with configuration', () => {
      // Given: Configuration
      // When: Creating an IntegrationTestHarness instance
      const testHarness = new IntegrationTestHarness(DEFAULT_TEST_CONFIG);

      // Then: The instance should be created successfully
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });

    it('should accept partial configuration', () => {
      // Given: Partial configuration
      const partialConfig = {
        api: {
          baseUrl: 'http://localhost:3000',
        },
      };

      // When: Creating an IntegrationTestHarness with partial config
      const testHarness = createTestHarness(partialConfig);

      // Then: The instance should be created successfully
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });

    it('should merge default configuration with custom configuration', () => {
      // Given: Custom configuration
      const customConfig = {
        api: {
          baseUrl: 'http://localhost:8080',
          port: 8080,
        },
        timeouts: {
          setup: 60000,
        },
      };

      // When: Creating an IntegrationTestHarness with custom config
      const testHarness = createTestHarness(customConfig);

      // Then: The configuration should be merged correctly
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });
  });

  describe('Accessors', () => {
    it('should provide access to database manager', () => {
      // Given: An IntegrationTestHarness instance
      // When: Getting the database manager
      const database = harness.getDatabase();

      // Then: The database manager should be returned
      expect(database).toBeInstanceOf(DatabaseManager);
    });

    it('should provide access to API client', () => {
      // Given: An IntegrationTestHarness instance
      // When: Getting the API client
      const api = harness.getApi();

      // Then: The API client should be returned
      expect(api).toBeInstanceOf(ApiClient);
    });

    it('should provide access to Docker manager', () => {
      // Given: An IntegrationTestHarness instance
      // When: Getting the Docker manager
      const docker = harness.getDocker();

      // Then: The Docker manager should be returned
      expect(docker).toBeDefined();
      expect(docker).toHaveProperty('start');
      expect(docker).toHaveProperty('stop');
    });

    it('should provide access to data factory', () => {
      // Given: An IntegrationTestHarness instance
      // When: Getting the data factory
      const factory = harness.getFactory();

      // Then: The data factory should be returned
      expect(factory).toBeDefined();
      expect(factory).toHaveProperty('createLeague');
      expect(factory).toHaveProperty('createSeason');
      expect(factory).toHaveProperty('createDriver');
    });
  });

  describe('Setup Hooks', () => {
    it('should have beforeAll hook', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for beforeAll hook
      // Then: The hook should exist
      expect(harness.beforeAll).toBeDefined();
      expect(typeof harness.beforeAll).toBe('function');
    });

    it('should have beforeEach hook', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for beforeEach hook
      // Then: The hook should exist
      expect(harness.beforeEach).toBeDefined();
      expect(typeof harness.beforeEach).toBe('function');
    });
  });

  describe('Teardown Hooks', () => {
    it('should have afterAll hook', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for afterAll hook
      // Then: The hook should exist
      expect(harness.afterAll).toBeDefined();
      expect(typeof harness.afterAll).toBe('function');
    });

    it('should have afterEach hook', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for afterEach hook
      // Then: The hook should exist
      expect(harness.afterEach).toBeDefined();
      expect(typeof harness.afterEach).toBe('function');
    });
  });

  describe('Transaction Management', () => {
    it('should have withTransaction method', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for withTransaction method
      // Then: The method should exist
      expect(harness.withTransaction).toBeDefined();
      expect(typeof harness.withTransaction).toBe('function');
    });

    it('should execute callback within transaction', async () => {
      // Given: An IntegrationTestHarness instance
      // When: Executing withTransaction
      const result = await harness.withTransaction(async (db) => {
        // Execute a simple query
        const queryResult = await db.query('SELECT 1 as test_value');
        return queryResult.rows[0].test_value;
      });

      // Then: The callback should execute and return the result
      expect(result).toBe(1);
    });

    it('should rollback transaction after callback', async () => {
      // Given: An IntegrationTestHarness instance
      // When: Executing withTransaction
      await harness.withTransaction(async (db) => {
        // Execute a query
        await db.query('SELECT 1 as test_value');
        // The transaction should be rolled back after this
      });

      // Then: The transaction should be rolled back
      // (This is verified by the fact that no error is thrown)
      expect(true).toBe(true);
    });
  });

  describe('Constraint Violation Detection', () => {
    it('should have expectConstraintViolation method', () => {
      // Given: An IntegrationTestHarness instance
      // When: Checking for expectConstraintViolation method
      // Then: The method should exist
      expect(harness.expectConstraintViolation).toBeDefined();
      expect(typeof harness.expectConstraintViolation).toBe('function');
    });

    it('should detect constraint violations', async () => {
      // Given: An IntegrationTestHarness instance
      // When: Executing an operation that violates a constraint
      // Then: Should throw an error
      await expect(
        harness.expectConstraintViolation(async () => {
          // This operation should violate a constraint
          throw new Error('constraint violation: duplicate key');
        })
      ).rejects.toThrow('Expected constraint violation but operation succeeded');
    });

    it('should detect specific constraint violations', async () => {
      // Given: An IntegrationTestHarness instance
      // When: Executing an operation that violates a specific constraint
      // Then: Should throw an error with the expected constraint
      await expect(
        harness.expectConstraintViolation(
          async () => {
            // This operation should violate a specific constraint
            throw new Error('constraint violation: unique_violation');
          },
          'unique_violation'
        )
      ).rejects.toThrow('Expected constraint violation but operation succeeded');
    });

    it('should detect non-constraint errors', async () => {
      // Given: An IntegrationTestHarness instance
      // When: Executing an operation that throws a non-constraint error
      // Then: Should throw an error
      await expect(
        harness.expectConstraintViolation(async () => {
          // This operation should throw a non-constraint error
          throw new Error('Some other error');
        })
      ).rejects.toThrow('Expected constraint violation but got: Some other error');
    });
  });

  describe('Configuration', () => {
    it('should use default configuration', () => {
      // Given: Default configuration
      // When: Creating a harness with default config
      const testHarness = createTestHarness();

      // Then: The configuration should match defaults
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });

    it('should accept custom configuration', () => {
      // Given: Custom configuration
      const customConfig = {
        api: {
          baseUrl: 'http://localhost:9000',
          port: 9000,
        },
        database: {
          host: 'custom-host',
          port: 5434,
          database: 'custom_db',
          user: 'custom_user',
          password: 'custom_pass',
        },
        timeouts: {
          setup: 30000,
          teardown: 15000,
          test: 30000,
        },
      };

      // When: Creating a harness with custom config
      const testHarness = createTestHarness(customConfig);

      // Then: The configuration should be applied
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });

    it('should merge configuration correctly', () => {
      // Given: Partial configuration
      const partialConfig = {
        api: {
          baseUrl: 'http://localhost:8080',
        },
        timeouts: {
          setup: 60000,
        },
      };

      // When: Creating a harness with partial config
      const testHarness = createTestHarness(partialConfig);

      // Then: The configuration should be merged with defaults
      expect(testHarness).toBeInstanceOf(IntegrationTestHarness);
    });
  });

  describe('Default Configuration', () => {
    it('should have correct default API configuration', () => {
      // Given: Default configuration
      // When: Checking default API configuration
      // Then: Should match expected defaults
      expect(DEFAULT_TEST_CONFIG.api.baseUrl).toBe('http://localhost:3101');
      expect(DEFAULT_TEST_CONFIG.api.port).toBe(3101);
    });

    it('should have correct default database configuration', () => {
      // Given: Default configuration
      // When: Checking default database configuration
      // Then: Should match expected defaults
      expect(DEFAULT_TEST_CONFIG.database.host).toBe('localhost');
      expect(DEFAULT_TEST_CONFIG.database.port).toBe(5433);
      expect(DEFAULT_TEST_CONFIG.database.database).toBe('gridpilot_test');
      expect(DEFAULT_TEST_CONFIG.database.user).toBe('gridpilot_test_user');
      expect(DEFAULT_TEST_CONFIG.database.password).toBe('gridpilot_test_pass');
    });

    it('should have correct default timeouts', () => {
      // Given: Default configuration
      // When: Checking default timeouts
      // Then: Should match expected defaults
      expect(DEFAULT_TEST_CONFIG.timeouts.setup).toBe(120000);
      expect(DEFAULT_TEST_CONFIG.timeouts.teardown).toBe(30000);
      expect(DEFAULT_TEST_CONFIG.timeouts.test).toBe(60000);
    });
  });
});
