/**
 * Integration Test: Team Creation Use Case Orchestration
 *
 * Tests the orchestration logic of team creation-related Use Cases:
 * - CreateTeamUseCase: Creates a new team with name, description, and leagues
 * - Validates that Use Cases correctly interact with their Ports (Repositories)
 * - Uses In-Memory adapters for fast, deterministic testing
 *
 * Focus: Business logic orchestration, NOT UI rendering
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { InMemoryTeamRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryTeamRepository';
import { InMemoryTeamMembershipRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryTeamMembershipRepository';
import { CreateTeamUseCase } from '../../../core/racing/application/use-cases/CreateTeamUseCase';
import { Team } from '../../../core/racing/domain/entities/Team';
import { Driver } from '../../../core/racing/domain/entities/Driver';
import { League } from '../../../core/racing/domain/entities/League';
import { Logger } from '../../../core/shared/domain/Logger';

describe('Team Creation Use Case Orchestration', () => {
  let teamRepository: InMemoryTeamRepository;
  let membershipRepository: InMemoryTeamMembershipRepository;
  let createTeamUseCase: CreateTeamUseCase;
  let mockLogger: Logger;

  beforeAll(() => {
    mockLogger = {
      info: () => {},
      debug: () => {},
      warn: () => {},
      error: () => {},
    } as unknown as Logger;
    
    teamRepository = new InMemoryTeamRepository(mockLogger);
    membershipRepository = new InMemoryTeamMembershipRepository(mockLogger);
    createTeamUseCase = new CreateTeamUseCase(teamRepository, membershipRepository, mockLogger);
  });

  beforeEach(() => {
    teamRepository.clear();
    membershipRepository.clear();
  });

  describe('CreateTeamUseCase - Success Path', () => {
    it('should create a team with all required fields', async () => {
      // Scenario: Team creation with complete information
      // Given: A driver exists
      const driverId = 'd1';
      const driver = Driver.create({ id: driverId, iracingId: '1', name: 'John Doe', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l1';
      const league = League.create({ id: leagueId, name: 'League 1', description: 'Test League', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with valid command
      const result = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: The team should be created successfully
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      
      // And: The team should have the correct properties
      expect(team.name.toString()).toBe('Test Team');
      expect(team.tag.toString()).toBe('TT');
      expect(team.description.toString()).toBe('A test team');
      expect(team.ownerId.toString()).toBe(driverId);
      expect(team.leagues.map(l => l.toString())).toContain(leagueId);
      
      // And: The team should be in the repository
      const savedTeam = await teamRepository.findById(team.id.toString());
      expect(savedTeam).toBeDefined();
      expect(savedTeam?.name.toString()).toBe('Test Team');
      
      // And: The driver should have an owner membership
      const membership = await membershipRepository.getMembership(team.id.toString(), driverId);
      expect(membership).toBeDefined();
      expect(membership?.role).toBe('owner');
      expect(membership?.status).toBe('active');
    });

    it('should create a team with optional description', async () => {
      // Scenario: Team creation with description
      // Given: A driver exists
      const driverId = 'd2';
      const driver = Driver.create({ id: driverId, iracingId: '2', name: 'Jane Doe', country: 'UK' });
      
      // And: A league exists
      const leagueId = 'l2';
      const league = League.create({ id: leagueId, name: 'League 2', description: 'Test League 2', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with description
      const result = await createTeamUseCase.execute({
        name: 'Team With Description',
        tag: 'TWD',
        description: 'This team has a detailed description',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: The team should be created with the description
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      expect(team.description.toString()).toBe('This team has a detailed description');
    });

    it('should create a team with minimal required fields', async () => {
      // Scenario: Team creation with minimal information
      // Given: A driver exists
      const driverId = 'd3';
      const driver = Driver.create({ id: driverId, iracingId: '3', name: 'Bob Smith', country: 'CA' });
      
      // And: A league exists
      const leagueId = 'l3';
      const league = League.create({ id: leagueId, name: 'League 3', description: 'Test League 3', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with only required fields
      const result = await createTeamUseCase.execute({
        name: 'Minimal Team',
        tag: 'MT',
        description: '',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: The team should be created with default values
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      expect(team.name.toString()).toBe('Minimal Team');
      expect(team.tag.toString()).toBe('MT');
      expect(team.description.toString()).toBe('');
    });
  });

  describe('CreateTeamUseCase - Validation', () => {
    it('should reject team creation with empty team name', async () => {
      // Scenario: Team creation with empty name
      // Given: A driver exists
      const driverId = 'd4';
      const driver = Driver.create({ id: driverId, iracingId: '4', name: 'Test Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l4';
      const league = League.create({ id: leagueId, name: 'League 4', description: 'Test League 4', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with empty team name
      const result = await createTeamUseCase.execute({
        name: '',
        tag: 'TT',
        description: 'A test team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
    });

    it('should reject team creation with invalid team name format', async () => {
      // Scenario: Team creation with invalid name format
      // Given: A driver exists
      const driverId = 'd5';
      const driver = Driver.create({ id: driverId, iracingId: '5', name: 'Test Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l5';
      const league = League.create({ id: leagueId, name: 'League 5', description: 'Test League 5', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with invalid team name
      const result = await createTeamUseCase.execute({
        name: 'Invalid!@#$%',
        tag: 'TT',
        description: 'A test team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
    });

    it('should reject team creation when driver already belongs to a team', async () => {
      // Scenario: Driver already belongs to a team
      // Given: A driver exists
      const driverId = 'd6';
      const driver = Driver.create({ id: driverId, iracingId: '6', name: 'Test Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l6';
      const league = League.create({ id: leagueId, name: 'League 6', description: 'Test League 6', ownerId: 'owner' });
      
      // And: The driver already belongs to a team
      const existingTeam = Team.create({ id: 'existing', name: 'Existing Team', tag: 'ET', description: 'Existing', ownerId: driverId, leagues: [] });
      await teamRepository.create(existingTeam);
      await membershipRepository.saveMembership({
        teamId: 'existing',
        driverId: driverId,
        role: 'driver',
        status: 'active',
        joinedAt: new Date()
      });
      
      // When: CreateTeamUseCase.execute() is called
      const result = await createTeamUseCase.execute({
        name: 'New Team',
        tag: 'NT',
        description: 'A new team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('already belongs to a team');
    });
  });

  describe('CreateTeamUseCase - Error Handling', () => {
    it('should throw error when driver does not exist', async () => {
      // Scenario: Non-existent driver
      // Given: No driver exists with the given ID
      const nonExistentDriverId = 'nonexistent';
      
      // And: A league exists
      const leagueId = 'l7';
      const league = League.create({ id: leagueId, name: 'League 7', description: 'Test League 7', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called with non-existent driver ID
      const result = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: nonExistentDriverId,
        leagues: [leagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
    });

    it('should throw error when league does not exist', async () => {
      // Scenario: Non-existent league
      // Given: A driver exists
      const driverId = 'd8';
      const driver = Driver.create({ id: driverId, iracingId: '8', name: 'Test Driver', country: 'US' });
      
      // And: No league exists with the given ID
      const nonExistentLeagueId = 'nonexistent';
      
      // When: CreateTeamUseCase.execute() is called with non-existent league ID
      const result = await createTeamUseCase.execute({
        name: 'Test Team',
        tag: 'TT',
        description: 'A test team',
        ownerId: driverId,
        leagues: [nonExistentLeagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('LEAGUE_NOT_FOUND');
    });

    it('should throw error when team name already exists', async () => {
      // Scenario: Duplicate team name
      // Given: A driver exists
      const driverId = 'd9';
      const driver = Driver.create({ id: driverId, iracingId: '9', name: 'Test Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l9';
      const league = League.create({ id: leagueId, name: 'League 9', description: 'Test League 9', ownerId: 'owner' });
      
      // And: A team with the same name already exists
      const existingTeam = Team.create({ id: 'existing2', name: 'Duplicate Team', tag: 'DT', description: 'Existing', ownerId: 'other', leagues: [] });
      await teamRepository.create(existingTeam);
      
      // When: CreateTeamUseCase.execute() is called with duplicate team name
      const result = await createTeamUseCase.execute({
        name: 'Duplicate Team',
        tag: 'DT2',
        description: 'A new team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: Should return error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('already exists');
    });
  });

  describe('CreateTeamUseCase - Business Logic', () => {
    it('should set the creating driver as team captain', async () => {
      // Scenario: Driver becomes captain
      // Given: A driver exists
      const driverId = 'd10';
      const driver = Driver.create({ id: driverId, iracingId: '10', name: 'Captain Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l10';
      const league = League.create({ id: leagueId, name: 'League 10', description: 'Test League 10', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called
      const result = await createTeamUseCase.execute({
        name: 'Captain Team',
        tag: 'CT',
        description: 'A team with captain',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: The creating driver should be set as team captain
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      
      // And: The captain role should be recorded in the team roster
      const membership = await membershipRepository.getMembership(team.id.toString(), driverId);
      expect(membership).toBeDefined();
      expect(membership?.role).toBe('owner');
    });

    it('should generate unique team ID', async () => {
      // Scenario: Unique team ID generation
      // Given: A driver exists
      const driverId = 'd11';
      const driver = Driver.create({ id: driverId, iracingId: '11', name: 'Unique Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l11';
      const league = League.create({ id: leagueId, name: 'League 11', description: 'Test League 11', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called
      const result = await createTeamUseCase.execute({
        name: 'Unique Team',
        tag: 'UT',
        description: 'A unique team',
        ownerId: driverId,
        leagues: [leagueId]
      });

      // Then: The team should have a unique ID
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      expect(team.id.toString()).toBeDefined();
      expect(team.id.toString().length).toBeGreaterThan(0);
      
      // And: The ID should not conflict with existing teams
      const existingTeam = await teamRepository.findById(team.id.toString());
      expect(existingTeam).toBeDefined();
      expect(existingTeam?.id.toString()).toBe(team.id.toString());
    });

    it('should set creation timestamp', async () => {
      // Scenario: Creation timestamp
      // Given: A driver exists
      const driverId = 'd12';
      const driver = Driver.create({ id: driverId, iracingId: '12', name: 'Timestamp Driver', country: 'US' });
      
      // And: A league exists
      const leagueId = 'l12';
      const league = League.create({ id: leagueId, name: 'League 12', description: 'Test League 12', ownerId: 'owner' });
      
      // When: CreateTeamUseCase.execute() is called
      const beforeCreate = new Date();
      const result = await createTeamUseCase.execute({
        name: 'Timestamp Team',
        tag: 'TT',
        description: 'A team with timestamp',
        ownerId: driverId,
        leagues: [leagueId]
      });
      const afterCreate = new Date();

      // Then: The team should have a creation timestamp
      expect(result.isOk()).toBe(true);
      const { team } = result.unwrap();
      expect(team.createdAt).toBeDefined();
      
      // And: The timestamp should be current or recent
      const createdAt = team.createdAt.toDate();
      expect(createdAt.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime());
      expect(createdAt.getTime()).toBeLessThanOrEqual(afterCreate.getTime());
    });
  });
});
