/**
 * Integration tests for TabApiClient
 * Tests against real TAB API (requires valid API key in .env)
 *
 * Run with: npm run test:integration
 * Skip if no API key: SKIP_INTEGRATION_TESTS=true npm test
 */

import { TabApiClient } from '../tab-api-client';
import config from '../../../config';

// Skip integration tests if explicitly requested or no API key available
const shouldSkip = process.env.SKIP_INTEGRATION_TESTS === 'true' ||
                   config.tabApi.apiKey === 'your-tab-api-key';

const describeIntegration = shouldSkip ? describe.skip : describe;

describeIntegration('TabApiClient Integration Tests', () => {
  let client: TabApiClient;

  beforeAll(() => {
    // Create client with real configuration
    client = new TabApiClient({
      baseUrl: config.tabApi.baseUrl,
      timeout: 15000, // Longer timeout for real API
      maxRetries: 2,
      retryDelay: 2000,
      rateLimitPerMinute: 60, // Conservative for testing
    });
  });

  describe('getMeetings', () => {
    it('should fetch today\'s Australian thoroughbred meetings', async () => {
      const result = await client.getMeetings({
        category: 'T',
        country: 'AUS',
        dateFrom: 'today',
        dateTo: 'today',
      });

      // Validate response structure
      expect(result).toHaveProperty('header');
      expect(result).toHaveProperty('params');
      expect(result).toHaveProperty('data');

      expect(result.header).toHaveProperty('title');
      expect(result.header).toHaveProperty('generated_time');
      expect(result.header).toHaveProperty('url');

      expect(result.data).toHaveProperty('meetings');
      expect(Array.isArray(result.data.meetings)).toBe(true);

      // Log meeting count for visibility
      console.log(`Found ${result.data.meetings.length} meetings`);

      // If meetings exist, validate structure
      if (result.data.meetings.length > 0) {
        const meeting = result.data.meetings[0];

        expect(meeting).toHaveProperty('meeting');
        expect(meeting).toHaveProperty('name');
        expect(meeting).toHaveProperty('date');
        expect(meeting).toHaveProperty('track_condition');
        expect(meeting).toHaveProperty('category', 'T');
        expect(meeting).toHaveProperty('country', 'AUS');
        expect(meeting).toHaveProperty('races');

        expect(Array.isArray(meeting.races)).toBe(true);

        console.log(`First meeting: ${meeting.name} (${meeting.races.length} races)`);
      }
    }, 30000); // 30s timeout for API calls

    it('should fetch New Zealand harness meetings', async () => {
      const result = await client.getMeetings({
        category: 'H',
        country: 'NZ',
        dateFrom: 'today',
        dateTo: 'today',
      });

      expect(result.data.meetings).toBeDefined();
      expect(Array.isArray(result.data.meetings)).toBe(true);

      console.log(`Found ${result.data.meetings.length} NZ harness meetings`);

      // Validate category if meetings exist
      if (result.data.meetings.length > 0) {
        expect(result.data.meetings[0].category).toBe('H');
        expect(result.data.meetings[0].country).toBe('NZ');
      }
    }, 30000);

    it('should respect pagination with limit and offset', async () => {
      // Fetch first 2 meetings
      const page1 = await client.getMeetings({
        dateFrom: 'today',
        dateTo: 'week',
        limit: 2,
        offset: 0,
      });

      // Fetch next 2 meetings
      const page2 = await client.getMeetings({
        dateFrom: 'today',
        dateTo: 'week',
        limit: 2,
        offset: 2,
      });

      expect(page1.data.meetings.length).toBeLessThanOrEqual(2);
      expect(page2.data.meetings.length).toBeLessThanOrEqual(2);

      // If both have results, they should be different meetings
      if (page1.data.meetings.length > 0 && page2.data.meetings.length > 0) {
        expect(page1.data.meetings[0].meeting).not.toBe(page2.data.meetings[0].meeting);
      }
    }, 30000);

    it('should handle date range queries', async () => {
      const result = await client.getMeetings({
        category: 'T',
        dateFrom: 'today',
        dateTo: 'week',
      });

      expect(result.data.meetings).toBeDefined();
      console.log(`Found ${result.data.meetings.length} meetings in week range`);
    }, 30000);
  });

  describe('getMeetingById', () => {
    let testMeetingId: string;

    beforeAll(async () => {
      // Get a meeting ID to test with
      const meetings = await client.getMeetings({
        category: 'T',
        country: 'AUS',
        dateFrom: 'today',
        dateTo: 'today',
        limit: 1,
      });

      if (meetings.data.meetings.length > 0) {
        testMeetingId = meetings.data.meetings[0].meeting;
      }
    }, 30000);

    it('should fetch meeting details by ID', async () => {
      if (!testMeetingId) {
        console.warn('Skipping test - no meetings available today');
        return;
      }

      const result = await client.getMeetingById(testMeetingId);

      expect(result).toHaveProperty('header');
      expect(result).toHaveProperty('params');
      expect(result).toHaveProperty('data');

      expect(result.data.meetings).toHaveLength(1);

      const meeting = result.data.meetings[0];
      expect(meeting.meeting).toBe(testMeetingId);
      expect(meeting).toHaveProperty('name');
      expect(meeting).toHaveProperty('races');

      console.log(`Meeting details: ${meeting.name} with ${meeting.races.length} races`);

      // Validate race details
      if (meeting.races.length > 0) {
        const race = meeting.races[0];

        expect(race).toHaveProperty('id');
        expect(race).toHaveProperty('race_number');
        expect(race).toHaveProperty('name');
        expect(race).toHaveProperty('start_time');
        expect(race).toHaveProperty('distance');
        expect(race).toHaveProperty('status');

        console.log(`First race: R${race.race_number} - ${race.name} (${race.distance}m)`);
      }
    }, 30000);

    it('should throw error for non-existent meeting ID', async () => {
      const fakeId = '00000000-0000-0000-0000-000000000000';

      await expect(client.getMeetingById(fakeId)).rejects.toThrow();
    }, 30000);
  });

  describe('rate limiting', () => {
    it('should handle multiple concurrent requests', async () => {
      const requests = Array.from({ length: 5 }, () =>
        client.getMeetings({
          category: 'T',
          dateFrom: 'today',
          dateTo: 'today',
          limit: 1,
        })
      );

      const results = await Promise.all(requests);

      expect(results).toHaveLength(5);
      results.forEach(result => {
        expect(result.data.meetings).toBeDefined();
      });

      console.log('Successfully handled 5 concurrent requests');
    }, 45000);
  });

  describe('error handling and resilience', () => {
    it('should handle timeout gracefully', async () => {
      // Create client with very short timeout
      const timeoutClient = new TabApiClient({
        baseUrl: config.tabApi.baseUrl,
        timeout: 1, // 1ms - guaranteed to timeout
        maxRetries: 0,
      });

      await expect(
        timeoutClient.getMeetings({ limit: 1 })
      ).rejects.toThrow();
    }, 10000);
  });

  describe('observability', () => {
    it('should emit metrics for successful requests', async () => {
      const result = await client.getMeetings({
        category: 'T',
        limit: 1,
      });

      expect(result).toBeDefined();

      // Metrics are emitted via prom-client
      // In a real environment, these would be scraped by Prometheus
      console.log('Request completed - metrics emitted');
    }, 30000);

    it('should provide rate limiter status', () => {
      const status = client.getRateLimitStatus();

      expect(status).toHaveProperty('running');
      expect(status).toHaveProperty('queued');

      expect(typeof status.running).toBe('number');
      expect(typeof status.queued).toBe('number');

      console.log('Rate limiter status:', status);
    });
  });
});

// Additional test suite for local testing without real API
describe('TabApiClient Mock Integration Tests', () => {
  it('should create client with config from environment', () => {
    const client = new TabApiClient({
      baseUrl: config.tabApi.baseUrl,
      timeout: 10000,
      maxRetries: config.rateLimiting.retryAttempts,
      retryDelay: config.rateLimiting.retryDelayMs,
      rateLimitPerMinute: config.rateLimiting.perMinute,
    });

    expect(client).toBeDefined();
    expect(client.getRateLimitStatus).toBeDefined();
  });

  it('should validate UUID format before making requests', async () => {
    const client = new TabApiClient({
      baseUrl: 'https://api.tab.com.au',
    });

    await expect(client.getMeetingById('not-a-uuid')).rejects.toThrow('Invalid meeting ID format');
  });
});
