/**
 * Integration Test: Database Constraints and Error Mapping
 * 
 * Tests that the application properly handles and maps database constraint violations
 * using In-Memory adapters for fast, deterministic testing.
 * 
 * Focus: Business logic orchestration, NOT API endpoints
 */

import { describe, it, expect, beforeEach } from 'vitest';

// Mock data types that match what the use cases expect
interface DriverData {
  id: string;
  iracingId: string;
  name: string;
  country: string;
  bio?: string;
  joinedAt: Date;
  category?: string;
}

interface TeamData {
  id: string;
  name: string;
  tag: string;
  description: string;
  ownerId: string;
  leagues: string[];
  category?: string;
  isRecruiting: boolean;
  createdAt: Date;
}

interface TeamMembership {
  teamId: string;
  driverId: string;
  role: 'owner' | 'manager' | 'driver';
  status: 'active' | 'pending' | 'none';
  joinedAt: Date;
}

// Simple in-memory repositories for testing
class TestDriverRepository {
  private drivers = new Map<string, DriverData>();
  
  async findById(id: string): Promise<DriverData | null> {
    return this.drivers.get(id) || null;
  }
  
  async create(driver: DriverData): Promise<DriverData> {
    if (this.drivers.has(driver.id)) {
      throw new Error('Driver already exists');
    }
    this.drivers.set(driver.id, driver);
    return driver;
  }
  
  clear(): void {
    this.drivers.clear();
  }
}

class TestTeamRepository {
  private teams = new Map<string, TeamData>();
  
  async findById(id: string): Promise<TeamData | null> {
    return this.teams.get(id) || null;
  }
  
  async create(team: TeamData): Promise<TeamData> {
    // Check for duplicate team name/tag
    for (const existing of this.teams.values()) {
      if (existing.name === team.name && existing.tag === team.tag) {
        const error: any = new Error('Team already exists');
        error.code = 'DUPLICATE_TEAM';
        throw error;
      }
    }
    this.teams.set(team.id, team);
    return team;
  }
  
  async findAll(): Promise<TeamData[]> {
    return Array.from(this.teams.values());
  }
  
  clear(): void {
    this.teams.clear();
  }
}

class TestTeamMembershipRepository {
  private memberships = new Map<string, TeamMembership[]>();
  
  async getMembership(teamId: string, driverId: string): Promise<TeamMembership | null> {
    const teamMemberships = this.memberships.get(teamId) || [];
    return teamMemberships.find(m => m.driverId === driverId) || null;
  }
  
  async getActiveMembershipForDriver(driverId: string): Promise<TeamMembership | null> {
    for (const teamMemberships of this.memberships.values()) {
      const active = teamMemberships.find(m => m.driverId === driverId && m.status === 'active');
      if (active) return active;
    }
    return null;
  }
  
  async saveMembership(membership: TeamMembership): Promise<TeamMembership> {
    const teamMemberships = this.memberships.get(membership.teamId) || [];
    const existingIndex = teamMemberships.findIndex(
      m => m.driverId === membership.driverId
    );
    
    if (existingIndex >= 0) {
      // Check if already active
      const existing = teamMemberships[existingIndex];
      if (existing.status === 'active') {
        const error: any = new Error('Already a member');
        error.code = 'ALREADY_MEMBER';
        throw error;
      }
      teamMemberships[existingIndex] = membership;
    } else {
      teamMemberships.push(membership);
    }
    
    this.memberships.set(membership.teamId, teamMemberships);
    return membership;
  }
  
  clear(): void {
    this.memberships.clear();
  }
}

// Mock use case implementations
class CreateTeamUseCase {
  constructor(
    private teamRepository: TestTeamRepository,
    private membershipRepository: TestTeamMembershipRepository
  ) {}
  
  async execute(input: {
    name: string;
    tag: string;
    description: string;
    ownerId: string;
    leagues: string[];
  }): Promise<{ isOk: () => boolean; isErr: () => boolean; error?: any }> {
    try {
      // Check if driver already belongs to a team
      const existingMembership = await this.membershipRepository.getActiveMembershipForDriver(input.ownerId);
      if (existingMembership) {
        return {
          isOk: () => false,
          isErr: () => true,
          error: { code: 'VALIDATION_ERROR', details: { message: 'Driver already belongs to a team' } }
        };
      }
      
      const teamId = `team-${Date.now()}`;
      const team: TeamData = {
        id: teamId,
        name: input.name,
        tag: input.tag,
        description: input.description,
        ownerId: input.ownerId,
        leagues: input.leagues,
        isRecruiting: false,
        createdAt: new Date(),
      };
      
      await this.teamRepository.create(team);
      
      // Create owner membership
      const membership: TeamMembership = {
        teamId: team.id,
        driverId: input.ownerId,
        role: 'owner',
        status: 'active',
        joinedAt: new Date(),
      };
      
      await this.membershipRepository.saveMembership(membership);
      
      return {
        isOk: () => true,
        isErr: () => false,
      };
    } catch (error: any) {
      return {
        isOk: () => false,
        isErr: () => true,
        error: { code: error.code || 'REPOSITORY_ERROR', details: { message: error.message } }
      };
    }
  }
}

class JoinTeamUseCase {
  constructor(
    private teamRepository: TestTeamRepository,
    private membershipRepository: TestTeamMembershipRepository
  ) {}
  
  async execute(input: {
    teamId: string;
    driverId: string;
  }): Promise<{ isOk: () => boolean; isErr: () => boolean; error?: any }> {
    try {
      // Check if driver already belongs to a team
      const existingActive = await this.membershipRepository.getActiveMembershipForDriver(input.driverId);
      if (existingActive) {
        return {
          isOk: () => false,
          isErr: () => true,
          error: { code: 'ALREADY_IN_TEAM', details: { message: 'Driver already belongs to a team' } }
        };
      }
      
      // Check if already has membership (pending or active)
      const existingMembership = await this.membershipRepository.getMembership(input.teamId, input.driverId);
      if (existingMembership) {
        return {
          isOk: () => false,
          isErr: () => true,
          error: { code: 'ALREADY_MEMBER', details: { message: 'Already a member or have a pending request' } }
        };
      }
      
      // Check if team exists
      const team = await this.teamRepository.findById(input.teamId);
      if (!team) {
        return {
          isOk: () => false,
          isErr: () => true,
          error: { code: 'TEAM_NOT_FOUND', details: { message: 'Team not found' } }
        };
      }
      
      // Check if driver exists
      // Note: In real implementation, this would check driver repository
      // For this test, we'll assume driver exists if we got this far
      
      const membership: TeamMembership = {
        teamId: input.teamId,
        driverId: input.driverId,
        role: 'driver',
        status: 'active',
        joinedAt: new Date(),
      };
      
      await this.membershipRepository.saveMembership(membership);
      
      return {
        isOk: () => true,
        isErr: () => false,
      };
    } catch (error: any) {
      return {
        isOk: () => false,
        isErr: () => true,
        error: { code: error.code || 'REPOSITORY_ERROR', details: { message: error.message } }
      };
    }
  }
}

describe('Database Constraints - Use Case Integration', () => {
  let driverRepository: TestDriverRepository;
  let teamRepository: TestTeamRepository;
  let teamMembershipRepository: TestTeamMembershipRepository;
  let createTeamUseCase: CreateTeamUseCase;
  let joinTeamUseCase: JoinTeamUseCase;

  beforeEach(() => {
    driverRepository = new TestDriverRepository();
    teamRepository = new TestTeamRepository();
    teamMembershipRepository = new TestTeamMembershipRepository();
    
    createTeamUseCase = new CreateTeamUseCase(teamRepository, teamMembershipRepository);
    joinTeamUseCase = new JoinTeamUseCase(teamRepository, teamMembershipRepository);
  });

  describe('Unique Constraint Violations', () => {
    it('should handle duplicate team creation gracefully', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // And: A team is created successfully
      const teamResult1 = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: driver.id,
        leagues: [],
      });
      expect(teamResult1.isOk()).toBe(true);

      // When: Attempt to create the same team again (same name/tag)
      const teamResult2 = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'Another test team',
        ownerId: driver.id,
        leagues: [],
      });

      // Then: Should fail with appropriate error
      expect(teamResult2.isErr()).toBe(true);
      if (teamResult2.isErr()) {
        expect(teamResult2.error.code).toBe('DUPLICATE_TEAM');
      }
    });

    it('should handle duplicate membership gracefully', async () => {
      // Given: A driver and team exist
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      const team: TeamData = {
        id: 'team-123',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'other-driver',
        leagues: [],
        isRecruiting: false,
        createdAt: new Date(),
      };
      await teamRepository.create(team);

      // And: Driver joins the team successfully
      const joinResult1 = await joinTeamUseCase.execute({
        teamId: team.id,
        driverId: driver.id,
      });
      expect(joinResult1.isOk()).toBe(true);

      // When: Driver attempts to join the same team again
      const joinResult2 = await joinTeamUseCase.execute({
        teamId: team.id,
        driverId: driver.id,
      });

      // Then: Should fail with appropriate error
      expect(joinResult2.isErr()).toBe(true);
      if (joinResult2.isErr()) {
        expect(joinResult2.error.code).toBe('ALREADY_MEMBER');
      }
    });
  });

  describe('Foreign Key Constraint Violations', () => {
    it('should handle non-existent driver in team creation', async () => {
      // Given: No driver exists with the given ID
      // When: Attempt to create a team with non-existent owner
      const result = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'non-existent-driver',
        leagues: [],
      });

      // Then: Should fail with appropriate error
      expect(result.isErr()).toBe(true);
      if (result.isErr()) {
        expect(result.error.code).toBe('VALIDATION_ERROR');
      }
    });

    it('should handle non-existent team in join request', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // When: Attempt to join non-existent team
      const result = await joinTeamUseCase.execute({
        teamId: 'non-existent-team',
        driverId: driver.id,
      });

      // Then: Should fail with appropriate error
      expect(result.isErr()).toBe(true);
      if (result.isErr()) {
        expect(result.error.code).toBe('TEAM_NOT_FOUND');
      }
    });
  });

  describe('Data Integrity After Failed Operations', () => {
    it('should maintain repository state after constraint violations', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // And: A valid team is created
      const validTeamResult = await createTeamUseCase.execute({
        name: 'Valid Team',
        tag: 'VT',
        description: 'Valid team',
        ownerId: driver.id,
        leagues: [],
      });
      expect(validTeamResult.isOk()).toBe(true);

      // When: Attempt to create duplicate team (should fail)
      const duplicateResult = await createTeamUseCase.execute({
        name: 'Valid Team',
        tag: 'VT',
        description: 'Duplicate team',
        ownerId: driver.id,
        leagues: [],
      });
      expect(duplicateResult.isErr()).toBe(true);

      // Then: Original team should still exist and be retrievable
      const teams = await teamRepository.findAll();
      expect(teams.length).toBe(1);
      expect(teams[0].name).toBe('Valid Team');
    });

    it('should handle multiple failed operations without corruption', async () => {
      // Given: A driver and team exist
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      const team: TeamData = {
        id: 'team-123',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'other-driver',
        leagues: [],
        isRecruiting: false,
        createdAt: new Date(),
      };
      await teamRepository.create(team);

      // When: Multiple failed operations occur
      await joinTeamUseCase.execute({ teamId: 'non-existent', driverId: driver.id });
      await joinTeamUseCase.execute({ teamId: team.id, driverId: 'non-existent' });
      await createTeamUseCase.execute({ name: 'Test Team', tag: 'TT', description: 'Duplicate', ownerId: driver.id, leagues: [] });

      // Then: Repositories should remain in valid state
      const drivers = await driverRepository.findById(driver.id);
      const teams = await teamRepository.findAll();
      const membership = await teamMembershipRepository.getMembership(team.id, driver.id);

      expect(drivers).not.toBeNull();
      expect(teams.length).toBe(1);
      expect(membership).toBeNull(); // No successful joins
    });
  });

  describe('Concurrent Operations', () => {
    it('should handle concurrent team creation attempts safely', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // When: Multiple concurrent attempts to create teams with same name
      const concurrentRequests = Array(5).fill(null).map((_, i) => 
        createTeamUseCase.execute({
          name: 'Concurrent Team',
          tag: `CT${i}`,
          description: 'Concurrent creation',
          ownerId: driver.id,
          leagues: [],
        })
      );

      const results = await Promise.all(concurrentRequests);
      
      // Then: Exactly one should succeed, others should fail
      const successes = results.filter(r => r.isOk());
      const failures = results.filter(r => r.isErr());
      
      expect(successes.length).toBe(1);
      expect(failures.length).toBe(4);
      
      // All failures should be duplicate errors
      failures.forEach(result => {
        if (result.isErr()) {
          expect(result.error.code).toBe('DUPLICATE_TEAM');
        }
      });
    });

    it('should handle concurrent join requests safely', async () => {
      // Given: A driver and team exist
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      const team: TeamData = {
        id: 'team-123',
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: 'other-driver',
        leagues: [],
        isRecruiting: false,
        createdAt: new Date(),
      };
      await teamRepository.create(team);

      // When: Multiple concurrent join attempts
      const concurrentJoins = Array(3).fill(null).map(() => 
        joinTeamUseCase.execute({
          teamId: team.id,
          driverId: driver.id,
        })
      );

      const results = await Promise.all(concurrentJoins);
      
      // Then: Exactly one should succeed
      const successes = results.filter(r => r.isOk());
      const failures = results.filter(r => r.isErr());
      
      expect(successes.length).toBe(1);
      expect(failures.length).toBe(2);
      
      // All failures should be already member errors
      failures.forEach(result => {
        if (result.isErr()) {
          expect(result.error.code).toBe('ALREADY_MEMBER');
        }
      });
    });
  });

  describe('Error Mapping and Reporting', () => {
    it('should provide meaningful error messages for constraint violations', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // And: A team is created
      await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'Test',
        ownerId: driver.id,
        leagues: [],
      });

      // When: Attempt to create duplicate
      const result = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'Duplicate',
        ownerId: driver.id,
        leagues: [],
      });

      // Then: Error should have clear message
      expect(result.isErr()).toBe(true);
      if (result.isErr()) {
        expect(result.error.details.message).toContain('already exists');
        expect(result.error.details.message).toContain('Test Team');
      }
    });

    it('should handle repository errors gracefully', async () => {
      // Given: A driver exists
      const driver: DriverData = {
        id: 'driver-123',
        iracingId: '12345',
        name: 'Test Driver',
        country: 'US',
        joinedAt: new Date(),
      };
      await driverRepository.create(driver);

      // When: Repository throws an error (simulated by using invalid data)
      // Note: In real scenario, this would be a database error
      // For this test, we'll verify the error handling path works
      const result = await createTeamUseCase.execute({
        name: '', // Invalid - empty name
        tag: 'TT',
        description: 'Test',
        ownerId: driver.id,
        leagues: [],
      });

      // Then: Should handle validation error
      expect(result.isErr()).toBe(true);
    });
  });
});