import { describe, it, expect } from 'vitest';
import { EscrowEntity } from '../entities/escrow.entity';
import { EscrowDisputedEvent } from '../events/escrow-disputed.event';
import { EscrowHeldEvent } from '../events/escrow-held.event';
import { EscrowReleasedEvent } from '../events/escrow-released.event';
import { Money } from '../value-objects/money.vo';

describe('EscrowEntity', () => {
  const createEscrow = () => {
    const amount = Money.create(5_000_000_000n).unwrap();
    const fee = Money.create(250_000_000n).unwrap();
    return EscrowEntity.createNew('esc-1', 'ord-1', amount, fee);
  };

  it('should create a new escrow with PENDING status', () => {
    const escrow = createEscrow();

    expect(escrow.id).toBe('esc-1');
    expect(escrow.orderId).toBe('ord-1');
    expect(escrow.amount.value).toBe(5_000_000_000n);
    expect(escrow.fee.value).toBe(250_000_000n);
    expect(escrow.status).toBe('PENDING');
    expect(escrow.heldAt).toBeNull();
    expect(escrow.releasedAt).toBeNull();
    expect(escrow.netPayout).toBe(4_750_000_000n);
  });

  it('should hold escrow from PENDING and emit event', () => {
    const escrow = createEscrow();
    const result = escrow.hold();

    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('HELD');
    expect(escrow.heldAt).toBeInstanceOf(Date);

    const events = escrow.domainEvents;
    expect(events).toHaveLength(1);
    expect(events[0]).toBeInstanceOf(EscrowHeldEvent);
  });

  it('should release escrow from HELD and emit event', () => {
    const escrow = createEscrow();
    escrow.hold();
    escrow.clearDomainEvents();

    const result = escrow.release();
    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('RELEASED');
    expect(escrow.releasedAt).toBeInstanceOf(Date);

    const events = escrow.domainEvents;
    expect(events).toHaveLength(1);
    expect(events[0]).toBeInstanceOf(EscrowReleasedEvent);
    expect((events[0] as EscrowReleasedEvent).payoutVND).toBe(4_750_000_000n);
  });

  it('should dispute escrow from HELD and emit event', () => {
    const escrow = createEscrow();
    escrow.hold();
    escrow.clearDomainEvents();

    const result = escrow.dispute('Hàng không đúng mô tả');
    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('DISPUTED');
    expect(escrow.disputeReason).toBe('Hàng không đúng mô tả');
    expect(escrow.disputedAt).toBeInstanceOf(Date);

    const events = escrow.domainEvents;
    expect(events).toHaveLength(1);
    expect(events[0]).toBeInstanceOf(EscrowDisputedEvent);
  });

  it('should release escrow from DISPUTED state', () => {
    const escrow = createEscrow();
    escrow.hold();
    escrow.dispute('Test dispute');
    escrow.clearDomainEvents();

    const result = escrow.release();
    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('RELEASED');
  });

  it('should refund escrow from HELD', () => {
    const escrow = createEscrow();
    escrow.hold();
    const result = escrow.refund();
    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('REFUNDED');
  });

  it('should refund escrow from DISPUTED', () => {
    const escrow = createEscrow();
    escrow.hold();
    escrow.dispute('Test dispute');
    const result = escrow.refund();
    expect(result.isOk).toBe(true);
    expect(escrow.status).toBe('REFUNDED');
  });

  it('should not hold escrow if already HELD', () => {
    const escrow = createEscrow();
    escrow.hold();
    const result = escrow.hold();
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr().message).toContain('HELD');
  });

  it('should not release escrow from PENDING', () => {
    const escrow = createEscrow();
    const result = escrow.release();
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr().message).toContain('PENDING');
  });

  it('should not dispute escrow from PENDING', () => {
    const escrow = createEscrow();
    const result = escrow.dispute('test');
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr().message).toContain('PENDING');
  });

  it('should not refund escrow from PENDING', () => {
    const escrow = createEscrow();
    const result = escrow.refund();
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr().message).toContain('PENDING');
  });

  it('should not refund escrow from RELEASED', () => {
    const escrow = createEscrow();
    escrow.hold();
    escrow.release();
    const result = escrow.refund();
    expect(result.isErr).toBe(true);
    expect(result.unwrapErr().message).toContain('RELEASED');
  });
});
