/**
 * QuestDB Client Integration Test
 *
 * This test validates that all components work together correctly
 * without requiring an actual QuestDB instance.
 */

import { afterEach, describe, expect, it } from 'bun:test';
import {
  QuestDBClient,
  QuestDBHealthMonitor,
  QuestDBInfluxWriter,
  QuestDBQueryBuilder,
  QuestDBSchemaManager,
} from '../src';
import { questdbTestHelpers } from './setup';

describe('QuestDB Client Integration', () => {
  let client: QuestDBClient;
  beforeEach(() => {
    client = new QuestDBClient({
      host: 'localhost',
      httpPort: 9000,
      pgPort: 8812,
      influxPort: 9009,
      database: 'questdb',
      user: 'admin',
      password: 'quest',
    });
  });
  afterEach(async () => {
    if (client && client.connected) {
      try {
        await client.disconnect();
      } catch {
        // Ignore cleanup errors in tests
      }
    }
  });

  describe('Client Initialization', () => {
    it('should create client with constructor', () => {
      const newClient = new QuestDBClient({
        host: 'localhost',
        httpPort: 9000,
        pgPort: 8812,
        influxPort: 9009,
        database: 'questdb',
        user: 'admin',
        password: 'quest',
      });
      expect(newClient).toBeInstanceOf(QuestDBClient);
    });

    it('should initialize all supporting classes', () => {
      expect(client.getHealthMonitor()).toBeInstanceOf(QuestDBHealthMonitor);
      expect(client.queryBuilder()).toBeInstanceOf(QuestDBQueryBuilder);
      expect(client.getInfluxWriter()).toBeInstanceOf(QuestDBInfluxWriter);
      expect(client.getSchemaManager()).toBeInstanceOf(QuestDBSchemaManager);
    });

    it('should handle connection configuration', () => {
      expect(client.getHttpUrl()).toBe('http://localhost:9000');
      expect(client.getInfluxUrl()).toBe('http://localhost:9009');
      expect(client.connected).toBe(false);
    });
  });

  describe('Query Builder', () => {
    it('should build query using query builder', () => {
      const query = client
        .queryBuilder()
        .select('symbol', 'close', 'timestamp')
        .from('ohlcv')
        .whereSymbol('AAPL')
        .whereLastHours(24)
        .orderBy('timestamp', 'DESC')
        .limit(100)
        .build();

      expect(query).toContain('SELECT symbol, close, timestamp');
      expect(query).toContain('FROM ohlcv');
      expect(query).toContain("symbol = 'AAPL'");
      expect(query).toContain('ORDER BY timestamp DESC');
      expect(query).toContain('LIMIT 100');
      expect(questdbTestHelpers.validateQuestDBQuery(query)).toBe(true);
    });

    it('should build time-series specific queries', () => {
      const latestQuery = client
        .queryBuilder()
        .select('*')
        .from('ohlcv')
        .latestBy('symbol')
        .build();

      expect(latestQuery).toContain('LATEST BY symbol');
      expect(questdbTestHelpers.validateQuestDBQuery(latestQuery)).toBe(true);

      const sampleQuery = client
        .queryBuilder()
        .select('symbol', 'avg(close)')
        .from('ohlcv')
        .sampleBy('1d')
        .build();

      expect(sampleQuery).toContain('SAMPLE BY 1d');
      expect(questdbTestHelpers.validateQuestDBQuery(sampleQuery)).toBe(true);
    });

    it('should build aggregation queries', () => {
      const query = client
        .aggregate('ohlcv')
        .select('symbol', 'avg(close) as avg_price', 'max(high) as max_high')
        .whereSymbolIn(['AAPL', 'GOOGL'])
        .groupBy('symbol')
        .sampleBy('1h')
        .build();

      expect(query).toContain('SELECT symbol, avg(close) as avg_price, max(high) as max_high');
      expect(query).toContain('FROM ohlcv');
      expect(query).toContain("symbol IN ('AAPL', 'GOOGL')");
      expect(query).toContain('SAMPLE BY 1h');
      expect(query).toContain('GROUP BY symbol');
      expect(questdbTestHelpers.validateQuestDBQuery(query)).toBe(true);
    });
  });
  describe('InfluxDB Writer', () => {
    it('should write OHLCV data using InfluxDB line protocol', async () => {
      const ohlcvData = [
        {
          timestamp: new Date('2024-01-01T12:00:00Z'),
          open: 150.0,
          high: 152.0,
          low: 149.5,
          close: 151.5,
          volume: 1000000,
        },
      ];

      // Mock the actual write operation
      const writeSpy = spyOn(client.getInfluxWriter(), 'writeOHLCV');
      writeSpy.mockReturnValue(Promise.resolve());
      await expect(async () => {
        await client.writeOHLCV('AAPL', 'NASDAQ', ohlcvData);
      }).not.toThrow();
    });

    it('should handle batch operations', () => {
      const lines = questdbTestHelpers.generateInfluxDBLines(3);
      expect(lines.length).toBe(3);

      lines.forEach(line => {
        expect(line).toContain('ohlcv,symbol=TEST');
        expect(line).toMatch(/\d{19}$/); // Nanosecond timestamp
      });
    });
  });

  describe('Schema Manager', () => {
    it('should provide schema access', () => {
      const schema = client.getSchemaManager().getSchema('ohlcv_data');

      expect(schema).toBeDefined();
      expect(schema?.tableName).toBe('ohlcv_data');

      const symbolColumn = schema?.columns.find(col => col.name === 'symbol');
      expect(symbolColumn).toBeDefined();
      expect(symbolColumn?.type).toBe('SYMBOL');

      expect(schema?.partitionBy).toBe('DAY');
    });
  });

  describe('Health Monitor', () => {
    it('should provide health monitoring capabilities', async () => {
      const healthMonitor = client.getHealthMonitor();
      expect(healthMonitor).toBeInstanceOf(QuestDBHealthMonitor);

      // Mock health status since we're not connected
      const mockHealthStatus = {
        isHealthy: false,
        lastCheck: new Date(),
        responseTime: 100,
        message: 'Connection not established',
        details: {
          pgPool: false,
          httpEndpoint: false,
          uptime: 0,
        },
      };

      const healthSpy = spyOn(healthMonitor, 'getHealthStatus');
      healthSpy.mockReturnValue(Promise.resolve(mockHealthStatus));

      const health = await healthMonitor.getHealthStatus();
      expect(health.isHealthy).toBe(false);
      expect(health.lastCheck).toBeInstanceOf(Date);
      expect(health.message).toBe('Connection not established');
    });
  });
  describe('Time-Series Operations', () => {
    it('should support latest by operations', async () => {
      // Mock the query execution
      const mockResult = {
        rows: [{ symbol: 'AAPL', close: 150.0, timestamp: new Date() }],
        rowCount: 1,
        executionTime: 10,
        metadata: { columns: [] },
      };

      const querySpy = spyOn(client, 'query');
      querySpy.mockReturnValue(Promise.resolve(mockResult));

      const result = await client.latestBy('ohlcv', ['symbol', 'close'], 'symbol');
      expect(result.rows.length).toBe(1);
      expect(result.rows[0].symbol).toBe('AAPL');
    });

    it('should support sample by operations', async () => {
      // Mock the query execution
      const mockResult = {
        rows: [{ symbol: 'AAPL', avg_close: 150.0, timestamp: new Date() }],
        rowCount: 1,
        executionTime: 15,
        metadata: { columns: [] },
      };

      const querySpy = spyOn(client, 'query');
      querySpy.mockReturnValue(Promise.resolve(mockResult));
      const result = await client.sampleBy(
        'ohlcv',
        ['symbol', 'avg(close) as avg_close'],
        '1h',
        'timestamp',
        "symbol = 'AAPL'"
      );

      expect(result.rows.length).toBe(1);
      expect(result.executionTime).toBe(15);
    });
  });

  describe('Connection Management', () => {
    it('should handle connection configuration', () => {
      expect(client.getHttpUrl()).toBe('http://localhost:9000');
      expect(client.getInfluxUrl()).toBe('http://localhost:9009');
      expect(client.connected).toBe(false);
    });

    it('should provide configuration access', () => {
      const config = client.configuration;
      expect(config.host).toBe('localhost');
      expect(config.httpPort).toBe(9000);
      expect(config.user).toBe('admin');
    });
  });
});
