/**
 * Contract Consumption Tests for Website
 * 
 * These tests validate that the website can properly consume and use
 * the generated API types without type errors.
 */

import { describe, it, expect, vi } from 'vitest';
import * as fs from 'fs/promises';
import * as path from 'path';
import { glob } from 'glob';

// Import all generated DTOs to ensure they compile
import type { RequestAvatarGenerationInputDTO } from './generated/RequestAvatarGenerationInputDTO';
import type { RequestAvatarGenerationOutputDTO } from './generated/RequestAvatarGenerationOutputDTO';
import type { UploadMediaInputDTO } from './generated/UploadMediaInputDTO';
import type { UploadMediaOutputDTO } from './generated/UploadMediaOutputDTO';
import type { GetMediaOutputDTO } from './generated/GetMediaOutputDTO';
import type { DeleteMediaOutputDTO } from './generated/DeleteMediaOutputDTO';
import type { GetAvatarOutputDTO } from './generated/GetAvatarOutputDTO';
import type { UpdateAvatarInputDTO } from './generated/UpdateAvatarInputDTO';
import type { UpdateAvatarOutputDTO } from './generated/UpdateAvatarOutputDTO';
import type { RaceDTO } from './generated/RaceDTO';
import type { DriverDTO } from './generated/DriverDTO';
import { LeaguesApiClient } from '../api/leagues/LeaguesApiClient';

describe('Website Contract Consumption', () => {
  const generatedTypesDir = path.join(__dirname, 'generated');

  describe('Generated Types Availability', () => {
    it('should have generated types directory', async () => {
      const exists = await fs.access(generatedTypesDir).then(() => true).catch(() => false);
      expect(exists).toBe(true);
    });

    it('should have expected DTO files', async () => {
      const requiredDTOs = [
        'RequestAvatarGenerationInputDTO.ts',
        'RequestAvatarGenerationOutputDTO.ts',
        'UploadMediaInputDTO.ts',
        'UploadMediaOutputDTO.ts',
        'GetMediaOutputDTO.ts',
        'DeleteMediaOutputDTO.ts',
        'GetAvatarOutputDTO.ts',
        'UpdateAvatarInputDTO.ts',
        'UpdateAvatarOutputDTO.ts',
        'RaceDTO.ts',
        'DriverDTO.ts'
      ];

      const files = await fs.readdir(generatedTypesDir);
      const tsFiles = files.filter(f => f.endsWith('.ts'));

      for (const required of requiredDTOs) {
        expect(tsFiles).toContain(required);
      }
    });

    it('should have no syntax errors in generated files', async () => {
      const files = await fs.readdir(generatedTypesDir);
      const dtos = files.filter(f => f.endsWith('.ts') && !f.endsWith('.test.ts'));

      for (const file of dtos) {
        const content = await fs.readFile(path.join(generatedTypesDir, file), 'utf-8');

        // `index.ts` is a generated barrel file (no interfaces).
        if (file === 'index.ts') {
          expect(content).toContain('export type {');
          expect(content).toContain("from './");
          continue;
        }

        // Basic syntax validation (DTO interfaces)
        expect(content).toContain('export interface');
        expect(content).toContain('{');
        expect(content).toContain('}');

        // Should not have common syntax errors
        expect(content).not.toMatch(/interface\s+\w+\s*\{\s*\}/); // Empty interfaces
      }
    });
  });

  describe('Type Compatibility', () => {
    it('should allow creating valid DTO objects', () => {
      // Test RequestAvatarGenerationInputDTO
      const avatarInput: RequestAvatarGenerationInputDTO = {
        userId: 'user-123',
        facePhotoData: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
        suitColor: 'red'
      };
      expect(avatarInput.userId).toBe('user-123');

      // Test RequestAvatarGenerationOutputDTO (success case)
      const avatarOutputSuccess: RequestAvatarGenerationOutputDTO = {
        success: true,
        requestId: 'req-123',
        avatarUrls: ['https://example.com/avatar1.png', 'https://example.com/avatar2.png']
      };
      expect(avatarOutputSuccess.success).toBe(true);
      expect(avatarOutputSuccess.avatarUrls).toHaveLength(2);

      // Test RequestAvatarGenerationOutputDTO (failure case)
      const avatarOutputFailure: RequestAvatarGenerationOutputDTO = {
        success: false,
        errorMessage: 'Generation failed'
      };
      expect(avatarOutputFailure.success).toBe(false);
      expect(avatarOutputFailure.errorMessage).toBe('Generation failed');
    });

    it('should handle optional fields correctly', () => {
      // UploadMediaInputDTO has a required `file` field in generated types
      const uploadInput: UploadMediaInputDTO = {
        file: {},
        type: 'image',
        category: 'avatar',
      };
      expect(uploadInput.type).toBe('image');

      // Minimal required fields
      const minimalUpload: UploadMediaInputDTO = {
        file: {},
        type: 'image',
      };
      expect(minimalUpload.type).toBe('image');
    });

    it('should support array types', () => {
      const avatarOutput: RequestAvatarGenerationOutputDTO = {
        success: true,
        requestId: 'req-123',
        avatarUrls: ['url1', 'url2', 'url3']
      };
      
      expect(Array.isArray(avatarOutput.avatarUrls)).toBe(true);
      expect(avatarOutput.avatarUrls?.length).toBeGreaterThan(0);
    });

    it('should support nested object types', () => {
      const race: RaceDTO = {
        id: 'race-123',
        name: 'Test Race',
        date: new Date().toISOString(),
        leagueName: 'Test League',
      };

      expect(race.id).toBe('race-123');
      expect(race.name).toBe('Test Race');
    });
  });

  describe('Service Integration', () => {
    it('should work with service layer patterns', () => {
      // Simulate a service method that uses DTOs
      function processAvatarRequest(input: RequestAvatarGenerationInputDTO): RequestAvatarGenerationOutputDTO {
        // Validate input
        if (!input.userId || !input.facePhotoData) {
          return {
            success: false,
            errorMessage: 'Missing required fields'
          };
        }

        return {
          success: true,
          requestId: `req-${Date.now()}`,
          avatarUrls: [`https://cdn.example.com/avatars/${input.userId}.png`]
        };
      }

      const result = processAvatarRequest({
        userId: 'test-user',
        facePhotoData: 'base64data',
        suitColor: 'blue'
      });

      expect(result.success).toBe(true);
      expect(result.avatarUrls).toBeDefined();
      expect(result.avatarUrls?.length).toBe(1);
    });

    it('should handle API response parsing', () => {
      // Simulate API response handling
      const mockApiResponse = {
        success: true,
        requestId: 'req-789',
        avatarUrls: ['https://cdn.example.com/avatar.png']
      };

      // Type assertion to ensure it matches DTO
      const parsedResponse: RequestAvatarGenerationOutputDTO = mockApiResponse;
      
      expect(parsedResponse.success).toBe(true);
      expect(parsedResponse.avatarUrls).toHaveLength(1);
    });
  });

  describe('Error Handling', () => {
    it('should normalize null races arrays to empty list', async () => {
      const originalFetch = globalThis.fetch;

      globalThis.fetch = vi.fn(async () => {
        return new Response(JSON.stringify({ races: null }), {
          status: 200,
          headers: { 'content-type': 'application/json' },
        });
      }) as any;

      const client = new LeaguesApiClient(
        'http://example.test',
        { report: vi.fn() } as any,
        { info: vi.fn(), debug: vi.fn(), warn: vi.fn(), error: vi.fn() } as any,
      );

      await expect(client.getRaces('league-1')).resolves.toEqual({ races: [] });

      globalThis.fetch = originalFetch;
    });

    it('should handle missing optional fields', () => {
      // Test that optional fields can be omitted
      const minimalOutput: RequestAvatarGenerationOutputDTO = {
        success: false,
        errorMessage: 'Error occurred'
      };

      expect(minimalOutput.success).toBe(false);
      expect(minimalOutput.errorMessage).toBe('Error occurred');
      // avatarUrls and requestId are optional in failure case
    });

    it('should handle URL|null pattern for media fields', () => {
      // Test that media fields use URL|null pattern, not empty strings
      const driverWithAvatar: DriverDTO = {
        id: 'driver-123',
        name: 'Test Driver',
        avatarUrl: 'https://example.com/avatar.png',
        country: 'US',
      };

      const driverWithoutAvatar: DriverDTO = {
        id: 'driver-456',
        name: 'No Avatar Driver',
        avatarUrl: null,
        country: 'UK',
      };

      expect(driverWithAvatar.avatarUrl).toBe('https://example.com/avatar.png');
      expect(driverWithoutAvatar.avatarUrl).toBeNull();
      
      // Should not use empty strings
      expect(driverWithAvatar.avatarUrl).not.toBe('');
      expect(driverWithoutAvatar.avatarUrl).not.toBeUndefined();
    });

    it('should allow type narrowing based on success flag', () => {
      function handleAvatarResponse(response: RequestAvatarGenerationOutputDTO) {
        if (response.success) {
          // Success case - avatarUrls should be available
          expect(response.avatarUrls).toBeDefined();
          expect(response.avatarUrls!.length).toBeGreaterThan(0);
        } else {
          // Failure case - errorMessage should be available
          expect(response.errorMessage).toBeDefined();
        }
      }

      handleAvatarResponse({
        success: true,
        requestId: 'req-1',
        avatarUrls: ['url1']
      });

      handleAvatarResponse({
        success: false,
        errorMessage: 'Failed'
      });
    });
  });
});
