import { LeadEntity, type LeadStatus } from '../entities/lead.entity';
import { LeadCreatedEvent } from '../events/lead-created.event';
import { LeadStatusChangedEvent } from '../events/lead-status-changed.event';
import { LeadScore } from '../value-objects/lead-score.vo';

describe('LeadEntity', () => {
  describe('createNew', () => {
    it('creates a lead with correct properties and NEW status', () => {
      const lead = LeadEntity.createNew(
        'lead-1',
        'agent-1',
        'Nguyễn Văn A',
        '0901234567',
        'a@example.com',
        'WEBSITE',
        null,
        null,
      );

      expect(lead.id).toBe('lead-1');
      expect(lead.agentId).toBe('agent-1');
      expect(lead.name).toBe('Nguyễn Văn A');
      expect(lead.phone).toBe('0901234567');
      expect(lead.email).toBe('a@example.com');
      expect(lead.source).toBe('WEBSITE');
      expect(lead.score).toBeNull();
      expect(lead.notes).toBeNull();
      expect(lead.status).toBe('NEW');
    });

    it('emits LeadCreatedEvent', () => {
      const lead = LeadEntity.createNew(
        'lead-1',
        'agent-1',
        'Nguyễn Văn A',
        '0901234567',
        null,
        'REFERRAL',
        null,
        null,
      );

      const events = lead.domainEvents;
      expect(events).toHaveLength(1);
      expect(events[0]).toBeInstanceOf(LeadCreatedEvent);

      const event = events[0] as LeadCreatedEvent;
      expect(event.aggregateId).toBe('lead-1');
      expect(event.agentId).toBe('agent-1');
    });
  });

  describe('updateStatus', () => {
    const createNewLead = (): LeadEntity =>
      LeadEntity.createNew(
        'lead-1',
        'agent-1',
        'Test',
        '0901234567',
        null,
        'WEBSITE',
        null,
        null,
      );

    it('allows NEW → CONTACTED transition', () => {
      const lead = createNewLead();
      lead.clearDomainEvents();

      lead.updateStatus('CONTACTED');

      expect(lead.status).toBe('CONTACTED');
      const events = lead.domainEvents;
      expect(events).toHaveLength(1);
      expect(events[0]).toBeInstanceOf(LeadStatusChangedEvent);
    });

    it('allows NEW → LOST transition', () => {
      const lead = createNewLead();
      lead.updateStatus('LOST');
      expect(lead.status).toBe('LOST');
    });

    it('allows CONTACTED → QUALIFIED transition', () => {
      const lead = createNewLead();
      lead.updateStatus('CONTACTED');
      lead.updateStatus('QUALIFIED');
      expect(lead.status).toBe('QUALIFIED');
    });

    it('allows QUALIFIED → NEGOTIATING transition', () => {
      const lead = createNewLead();
      lead.updateStatus('CONTACTED');
      lead.updateStatus('QUALIFIED');
      lead.updateStatus('NEGOTIATING');
      expect(lead.status).toBe('NEGOTIATING');
    });

    it('allows NEGOTIATING → CONVERTED transition', () => {
      const lead = createNewLead();
      lead.updateStatus('CONTACTED');
      lead.updateStatus('QUALIFIED');
      lead.updateStatus('NEGOTIATING');
      lead.updateStatus('CONVERTED');
      expect(lead.status).toBe('CONVERTED');
    });

    it('throws on CONVERTED → any transition', () => {
      const lead = createNewLead();
      lead.updateStatus('CONTACTED');
      lead.updateStatus('QUALIFIED');
      lead.updateStatus('NEGOTIATING');
      lead.updateStatus('CONVERTED');

      const statuses: LeadStatus[] = ['NEW', 'CONTACTED', 'QUALIFIED', 'NEGOTIATING', 'LOST'];
      for (const status of statuses) {
        expect(() => lead.updateStatus(status)).toThrow(
          `Không thể chuyển trạng thái từ CONVERTED sang ${status}`,
        );
      }
    });

    it('throws on LOST → any transition', () => {
      const lead = createNewLead();
      lead.updateStatus('LOST');

      const statuses: LeadStatus[] = ['NEW', 'CONTACTED', 'QUALIFIED', 'NEGOTIATING', 'CONVERTED'];
      for (const status of statuses) {
        expect(() => lead.updateStatus(status)).toThrow(
          `Không thể chuyển trạng thái từ LOST sang ${status}`,
        );
      }
    });

    it('throws on invalid transition NEW → QUALIFIED', () => {
      const lead = createNewLead();
      expect(() => lead.updateStatus('QUALIFIED')).toThrow(
        'Không thể chuyển trạng thái từ NEW sang QUALIFIED',
      );
    });

    it('emits LeadStatusChangedEvent with old and new status', () => {
      const lead = createNewLead();
      lead.clearDomainEvents();

      lead.updateStatus('CONTACTED');

      const events = lead.domainEvents;
      expect(events).toHaveLength(1);

      const event = events[0] as LeadStatusChangedEvent;
      expect(event.aggregateId).toBe('lead-1');
      expect(event.agentId).toBe('agent-1');
      expect(event.oldStatus).toBe('NEW');
      expect(event.newStatus).toBe('CONTACTED');
    });
  });
});

describe('LeadScore', () => {
  it('creates a valid score at 0', () => {
    const result = LeadScore.create(0);
    expect(result.isOk).toBe(true);
    expect(result.unwrap().value).toBe(0);
  });

  it('creates a valid score at 100', () => {
    const result = LeadScore.create(100);
    expect(result.isOk).toBe(true);
    expect(result.unwrap().value).toBe(100);
  });

  it('creates a valid score at 50', () => {
    const result = LeadScore.create(50);
    expect(result.isOk).toBe(true);
    expect(result.unwrap().value).toBe(50);
  });

  it('rejects negative score', () => {
    const result = LeadScore.create(-1);
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr()).toBe('Điểm lead phải từ 0 đến 100');
  });

  it('rejects score above 100', () => {
    const result = LeadScore.create(101);
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr()).toBe('Điểm lead phải từ 0 đến 100');
  });
});
