/**
 * Integration Test: Sponsor Signup Use Case Orchestration
 *
 * Tests the orchestration logic of sponsor signup-related Use Cases:
 * - CreateSponsorUseCase: Creates a new sponsor account
 * - 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 { InMemorySponsorRepository } from '../../../adapters/racing/persistence/inmemory/InMemorySponsorRepository';
import { CreateSponsorUseCase } from '../../../core/racing/application/use-cases/CreateSponsorUseCase';
import { Sponsor } from '../../../core/racing/domain/entities/sponsor/Sponsor';
import { Logger } from '../../../core/shared/domain/Logger';

describe('Sponsor Signup Use Case Orchestration', () => {
  let sponsorRepository: InMemorySponsorRepository;
  let createSponsorUseCase: CreateSponsorUseCase;
  let mockLogger: Logger;

  beforeAll(() => {
    mockLogger = {
      info: () => {},
      debug: () => {},
      warn: () => {},
      error: () => {},
    } as unknown as Logger;
    
    sponsorRepository = new InMemorySponsorRepository(mockLogger);
    createSponsorUseCase = new CreateSponsorUseCase(sponsorRepository, mockLogger);
  });

  beforeEach(() => {
    sponsorRepository.clear();
  });

  describe('CreateSponsorUseCase - Success Path', () => {
    it('should create a new sponsor account with valid information', async () => {
      // Given: No sponsor exists with the given email
      const sponsorId = 'sponsor-123';
      const sponsorData = {
        name: 'Test Company',
        contactEmail: 'test@example.com',
        websiteUrl: 'https://testcompany.com',
        logoUrl: 'https://testcompany.com/logo.png',
      };

      // When: CreateSponsorUseCase.execute() is called with valid sponsor data
      const result = await createSponsorUseCase.execute(sponsorData);

      // Then: The sponsor should be created successfully
      expect(result.isOk()).toBe(true);
      const createdSponsor = result.unwrap().sponsor;
      
      // And: The sponsor should have a unique ID
      expect(createdSponsor.id.toString()).toBeDefined();
      
      // And: The sponsor should have the provided company name
      expect(createdSponsor.name.toString()).toBe('Test Company');
      
      // And: The sponsor should have the provided contact email
      expect(createdSponsor.contactEmail.toString()).toBe('test@example.com');
      
      // And: The sponsor should have the provided website URL
      expect(createdSponsor.websiteUrl?.toString()).toBe('https://testcompany.com');
      
      // And: The sponsor should have the provided logo URL
      expect(createdSponsor.logoUrl?.toString()).toBe('https://testcompany.com/logo.png');
      
      // And: The sponsor should have a created timestamp
      expect(createdSponsor.createdAt).toBeDefined();
      
      // And: The sponsor should be retrievable from the repository
      const retrievedSponsor = await sponsorRepository.findById(createdSponsor.id.toString());
      expect(retrievedSponsor).toBeDefined();
      expect(retrievedSponsor?.name.toString()).toBe('Test Company');
    });

    it('should create a sponsor with minimal data', async () => {
      // Given: No sponsor exists
      const sponsorData = {
        name: 'Minimal Company',
        contactEmail: 'minimal@example.com',
      };

      // When: CreateSponsorUseCase.execute() is called with minimal data
      const result = await createSponsorUseCase.execute(sponsorData);

      // Then: The sponsor should be created successfully
      expect(result.isOk()).toBe(true);
      const createdSponsor = result.unwrap().sponsor;
      
      // And: The sponsor should have the provided company name
      expect(createdSponsor.name.toString()).toBe('Minimal Company');
      
      // And: The sponsor should have the provided contact email
      expect(createdSponsor.contactEmail.toString()).toBe('minimal@example.com');
      
      // And: Optional fields should be undefined
      expect(createdSponsor.websiteUrl).toBeUndefined();
      expect(createdSponsor.logoUrl).toBeUndefined();
    });

    it('should create a sponsor with optional fields only', async () => {
      // Given: No sponsor exists
      const sponsorData = {
        name: 'Optional Fields Company',
        contactEmail: 'optional@example.com',
        websiteUrl: 'https://optional.com',
      };

      // When: CreateSponsorUseCase.execute() is called with optional fields
      const result = await createSponsorUseCase.execute(sponsorData);

      // Then: The sponsor should be created successfully
      expect(result.isOk()).toBe(true);
      const createdSponsor = result.unwrap().sponsor;
      
      // And: The sponsor should have the provided website URL
      expect(createdSponsor.websiteUrl?.toString()).toBe('https://optional.com');
      
      // And: Logo URL should be undefined
      expect(createdSponsor.logoUrl).toBeUndefined();
    });
  });

  describe('CreateSponsorUseCase - Validation', () => {
    it('should reject sponsor creation with duplicate email', async () => {
      // Given: A sponsor exists with email "sponsor@example.com"
      const existingSponsor = Sponsor.create({
        id: 'existing-sponsor',
        name: 'Existing Company',
        contactEmail: 'sponsor@example.com',
      });
      await sponsorRepository.create(existingSponsor);

      // When: CreateSponsorUseCase.execute() is called with the same email
      const result = await createSponsorUseCase.execute({
        name: 'New Company',
        contactEmail: 'sponsor@example.com',
      });

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

    it('should reject sponsor creation with invalid email format', async () => {
      // Given: No sponsor exists
      // When: CreateSponsorUseCase.execute() is called with invalid email
      const result = await createSponsorUseCase.execute({
        name: 'Test Company',
        contactEmail: 'invalid-email',
      });

      // Then: Should return an error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('Invalid sponsor contact email format');
    });

    it('should reject sponsor creation with missing required fields', async () => {
      // Given: No sponsor exists
      // When: CreateSponsorUseCase.execute() is called without company name
      const result = await createSponsorUseCase.execute({
        name: '',
        contactEmail: 'test@example.com',
      });

      // Then: Should return an error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('Sponsor name is required');
    });

    it('should reject sponsor creation with invalid website URL', async () => {
      // Given: No sponsor exists
      // When: CreateSponsorUseCase.execute() is called with invalid URL
      const result = await createSponsorUseCase.execute({
        name: 'Test Company',
        contactEmail: 'test@example.com',
        websiteUrl: 'not-a-valid-url',
      });

      // Then: Should return an error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('Invalid sponsor website URL');
    });

    it('should reject sponsor creation with missing email', async () => {
      // Given: No sponsor exists
      // When: CreateSponsorUseCase.execute() is called without email
      const result = await createSponsorUseCase.execute({
        name: 'Test Company',
        contactEmail: '',
      });

      // Then: Should return an error
      expect(result.isErr()).toBe(true);
      const error = result.unwrapErr();
      expect(error.code).toBe('VALIDATION_ERROR');
      expect(error.details.message).toContain('Sponsor contact email is required');
    });
  });

  describe('Sponsor Data Orchestration', () => {
    it('should correctly create sponsor with all optional fields', async () => {
      // Given: No sponsor exists
      const sponsorData = {
        name: 'Full Featured Company',
        contactEmail: 'full@example.com',
        websiteUrl: 'https://fullfeatured.com',
        logoUrl: 'https://fullfeatured.com/logo.png',
      };

      // When: CreateSponsorUseCase.execute() is called with all fields
      const result = await createSponsorUseCase.execute(sponsorData);

      // Then: The sponsor should be created with all fields
      expect(result.isOk()).toBe(true);
      const createdSponsor = result.unwrap().sponsor;
      
      expect(createdSponsor.name.toString()).toBe('Full Featured Company');
      expect(createdSponsor.contactEmail.toString()).toBe('full@example.com');
      expect(createdSponsor.websiteUrl?.toString()).toBe('https://fullfeatured.com');
      expect(createdSponsor.logoUrl?.toString()).toBe('https://fullfeatured.com/logo.png');
      expect(createdSponsor.createdAt).toBeDefined();
    });

    it('should generate unique IDs for each sponsor', async () => {
      // Given: No sponsors exist
      const sponsorData1 = {
        name: 'Company 1',
        contactEmail: 'company1@example.com',
      };
      const sponsorData2 = {
        name: 'Company 2',
        contactEmail: 'company2@example.com',
      };

      // When: Creating two sponsors
      const result1 = await createSponsorUseCase.execute(sponsorData1);
      const result2 = await createSponsorUseCase.execute(sponsorData2);

      // Then: Both should succeed and have unique IDs
      expect(result1.isOk()).toBe(true);
      expect(result2.isOk()).toBe(true);
      
      const sponsor1 = result1.unwrap().sponsor;
      const sponsor2 = result2.unwrap().sponsor;
      
      expect(sponsor1.id.toString()).not.toBe(sponsor2.id.toString());
    });

    it('should persist sponsor in repository after creation', async () => {
      // Given: No sponsor exists
      const sponsorData = {
        name: 'Persistent Company',
        contactEmail: 'persistent@example.com',
      };

      // When: Creating a sponsor
      const result = await createSponsorUseCase.execute(sponsorData);

      // Then: The sponsor should be retrievable from the repository
      expect(result.isOk()).toBe(true);
      const createdSponsor = result.unwrap().sponsor;
      
      const retrievedSponsor = await sponsorRepository.findById(createdSponsor.id.toString());
      expect(retrievedSponsor).toBeDefined();
      expect(retrievedSponsor?.name.toString()).toBe('Persistent Company');
      expect(retrievedSponsor?.contactEmail.toString()).toBe('persistent@example.com');
    });
  });
});
