import type { EventBus } from '@nestjs/cqrs';
import { ReviewEntity } from '../../domain/entities/review.entity';
import type { IReviewRepository } from '../../domain/repositories/review.repository';
import { Rating } from '../../domain/value-objects/rating.vo';
import { CreateReviewCommand } from '../commands/create-review/create-review.command';
import { CreateReviewHandler } from '../commands/create-review/create-review.handler';

describe('CreateReviewHandler', () => {
  let handler: CreateReviewHandler;
  let mockReviewRepo: { [K in keyof IReviewRepository]: ReturnType<typeof vi.fn> };
  let mockEventBus: { publish: ReturnType<typeof vi.fn> };

  beforeEach(() => {
    mockReviewRepo = {
      findById: vi.fn(),
      findByUserAndTarget: vi.fn(),
      save: vi.fn(),
      delete: vi.fn(),
      findByTarget: vi.fn(),
      findByUserId: vi.fn(),
      getStats: vi.fn(),
    };

    mockEventBus = { publish: vi.fn() };

    const mockLogger = { log: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), verbose: vi.fn() };

    handler = new CreateReviewHandler(
      mockReviewRepo as any,
      mockEventBus as unknown as EventBus,
      mockLogger as any,
    );
  });

  it('creates a review successfully', async () => {
    mockReviewRepo.findByUserAndTarget.mockResolvedValue(null);
    mockReviewRepo.save.mockResolvedValue(undefined);

    const command = new CreateReviewCommand('user-1', 'agent', 'agent-1', 5, 'Great!');
    const result = await handler.execute(command);

    expect(result.rating).toBe(5);
    expect(result.targetType).toBe('agent');
    expect(result.targetId).toBe('agent-1');
    expect(result.id).toBeDefined();
    expect(mockReviewRepo.save).toHaveBeenCalledTimes(1);
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws ValidationException for invalid rating', async () => {
    const command = new CreateReviewCommand('user-1', 'agent', 'agent-1', 0, null);

    await expect(handler.execute(command)).rejects.toThrow();
  });

  it('throws ValidationException for rating > 5', async () => {
    const command = new CreateReviewCommand('user-1', 'agent', 'agent-1', 6, null);

    await expect(handler.execute(command)).rejects.toThrow();
  });

  it('prevents self-review for user target type', async () => {
    const command = new CreateReviewCommand('user-1', 'user', 'user-1', 5, null);

    await expect(handler.execute(command)).rejects.toThrow('Không thể tự đánh giá bản thân');
  });

  it('prevents duplicate review', async () => {
    const existingReview = new ReviewEntity('existing-1', {
      userId: 'user-1',
      targetType: 'agent',
      targetId: 'agent-1',
      rating: Rating.create(4).unwrap(),
      comment: null,
    });
    mockReviewRepo.findByUserAndTarget.mockResolvedValue(existingReview);

    const command = new CreateReviewCommand('user-1', 'agent', 'agent-1', 5, null);

    await expect(handler.execute(command)).rejects.toThrow('Bạn đã đánh giá mục tiêu này rồi');
  });

  it('allows review with null comment', async () => {
    mockReviewRepo.findByUserAndTarget.mockResolvedValue(null);
    mockReviewRepo.save.mockResolvedValue(undefined);

    const command = new CreateReviewCommand('user-1', 'agent', 'agent-1', 3, null);
    const result = await handler.execute(command);

    expect(result.rating).toBe(3);
  });
});
