import { SponsorshipRequest, SponsorableEntityType, SponsorshipRequestProps } from './SponsorshipRequest';
import { SponsorshipTier } from './season/SeasonSponsorship';
import { Money } from '../value-objects/Money';
import { RacingDomainValidationError, RacingDomainInvariantError } from '../errors/RacingDomainError';

describe('SponsorshipRequest', () => {
  const validMoney = Money.create(1000, 'USD');
  const validProps = {
    id: 'request-123',
    sponsorId: 'sponsor-456',
    entityType: 'driver',
    entityId: 'driver-789',
    tier: 'main',
    offeredAmount: validMoney,
  };

  describe('create', () => {
    it('should create a SponsorshipRequest with valid props', () => {
      const request = SponsorshipRequest.create(validProps);
      expect(request.id).toBe('request-123');
      expect(request.sponsorId).toBe('sponsor-456');
      expect(request.entityType).toBe('driver');
      expect(request.entityId).toBe('driver-789');
      expect(request.tier).toBe('main');
      expect(request.offeredAmount).toEqual(validMoney);
      expect(request.status).toBe('pending');
      expect(request.createdAt).toBeInstanceOf(Date);
    });

    it('should use provided createdAt and status', () => {
      const customDate = new Date('2023-01-01');
      const request = SponsorshipRequest.create({
        ...validProps,
        createdAt: customDate,
        status: 'accepted',
      });
      expect(request.createdAt).toBe(customDate);
      expect(request.status).toBe('accepted');
    });

    it('should throw for invalid id', () => {
      expect(() => SponsorshipRequest.create({ ...validProps, id: '' })).toThrow(RacingDomainValidationError);
    });

    it('should throw for invalid sponsorId', () => {
      expect(() => SponsorshipRequest.create({ ...validProps, sponsorId: '' })).toThrow(RacingDomainValidationError);
    });

    it('should throw for invalid entityId', () => {
      expect(() => SponsorshipRequest.create({ ...validProps, entityId: '' })).toThrow(RacingDomainValidationError);
    });

    it('should throw for zero offeredAmount', () => {
      const zeroMoney = Money.create(0, 'USD');
      expect(() => SponsorshipRequest.create({ ...validProps, offeredAmount: zeroMoney })).toThrow(RacingDomainValidationError);
    });

    it('should throw for negative offeredAmount', () => {
      expect(() => Money.create(-100, 'USD')).toThrow(RacingDomainValidationError);
    });
  });

  describe('accept', () => {
    it('should accept a pending request', () => {
      const request = SponsorshipRequest.create(validProps);
      const accepted = request.accept('responder-123');
      expect(accepted.status).toBe('accepted');
      expect(accepted.respondedAt).toBeInstanceOf(Date);
      expect(accepted.respondedBy).toBe('responder-123');
    });

    it('should throw for non-pending request', () => {
      const acceptedRequest = SponsorshipRequest.create({ ...validProps, status: 'accepted' });
      expect(() => acceptedRequest.accept('responder-123')).toThrow(RacingDomainInvariantError);
    });

    it('should throw for empty respondedBy', () => {
      const request = SponsorshipRequest.create(validProps);
      expect(() => request.accept('')).toThrow(RacingDomainValidationError);
    });
  });

  describe('reject', () => {
    it('should reject a pending request with reason', () => {
      const request = SponsorshipRequest.create(validProps);
      const rejected = request.reject('responder-123', 'Not interested');
      expect(rejected.status).toBe('rejected');
      expect(rejected.respondedAt).toBeInstanceOf(Date);
      expect(rejected.respondedBy).toBe('responder-123');
      expect(rejected.rejectionReason).toBe('Not interested');
    });

    it('should reject without reason', () => {
      const request = SponsorshipRequest.create(validProps);
      const rejected = request.reject('responder-123');
      expect(rejected.status).toBe('rejected');
      expect(rejected.rejectionReason).toBeUndefined();
    });

    it('should throw for non-pending request', () => {
      const rejectedRequest = SponsorshipRequest.create({ ...validProps, status: 'rejected' });
      expect(() => rejectedRequest.reject('responder-123')).toThrow(RacingDomainInvariantError);
    });
  });

  describe('withdraw', () => {
    it('should withdraw a pending request', () => {
      const request = SponsorshipRequest.create(validProps);
      const withdrawn = request.withdraw();
      expect(withdrawn.status).toBe('withdrawn');
      expect(withdrawn.respondedAt).toBeInstanceOf(Date);
    });

    it('should throw for non-pending request', () => {
      const acceptedRequest = SponsorshipRequest.create({ ...validProps, status: 'accepted' });
      expect(() => acceptedRequest.withdraw()).toThrow(RacingDomainInvariantError);
    });
  });

  describe('isPending', () => {
    it('should return true for pending status', () => {
      const request = SponsorshipRequest.create(validProps);
      expect(request.isPending()).toBe(true);
    });

    it('should return false for non-pending status', () => {
      const acceptedRequest = SponsorshipRequest.create({ ...validProps, status: 'accepted' });
      expect(acceptedRequest.isPending()).toBe(false);
    });
  });

  describe('isAccepted', () => {
    it('should return true for accepted status', () => {
      const acceptedRequest = SponsorshipRequest.create({ ...validProps, status: 'accepted' });
      expect(acceptedRequest.isAccepted()).toBe(true);
    });

    it('should return false for non-accepted status', () => {
      const request = SponsorshipRequest.create(validProps);
      expect(request.isAccepted()).toBe(false);
    });
  });

  describe('getPlatformFee', () => {
    it('should calculate platform fee', () => {
      const request = SponsorshipRequest.create(validProps);
      const fee = request.getPlatformFee();
      expect(fee.amount).toBe(100); // 10% of 1000
      expect(fee.currency).toBe('USD');
    });
  });

  describe('getNetAmount', () => {
    it('should calculate net amount', () => {
      const request = SponsorshipRequest.create(validProps);
      const net = request.getNetAmount();
      expect(net.amount).toBe(900); // 1000 - 100
      expect(net.currency).toBe('USD');
    });
  });
});