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 { DeleteReviewCommand } from '../commands/delete-review/delete-review.command';
import { DeleteReviewHandler } from '../commands/delete-review/delete-review.handler';

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

  const mockReview = new ReviewEntity('review-1', {
    userId: 'user-1',
    targetType: 'agent',
    targetId: 'agent-1',
    rating: Rating.create(4).unwrap(),
    comment: 'Good service',
  });

  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 DeleteReviewHandler(
      mockReviewRepo as any,
      mockEventBus as unknown as EventBus,
      mockLogger as any,
    );
  });

  it('deletes own review successfully', async () => {
    mockReviewRepo.findById.mockResolvedValue(mockReview);
    mockReviewRepo.delete.mockResolvedValue(undefined);

    const command = new DeleteReviewCommand('review-1', 'user-1');
    await handler.execute(command);

    expect(mockReviewRepo.delete).toHaveBeenCalledWith('review-1');
    expect(mockEventBus.publish).toHaveBeenCalled();
  });

  it('throws NotFoundException when review not found', async () => {
    mockReviewRepo.findById.mockResolvedValue(null);

    const command = new DeleteReviewCommand('nonexistent', 'user-1');

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

  it('throws ForbiddenException when deleting another user review', async () => {
    mockReviewRepo.findById.mockResolvedValue(mockReview);

    const command = new DeleteReviewCommand('review-1', 'user-2');

    await expect(handler.execute(command)).rejects.toThrow('Bạn chỉ có thể xóa đánh giá của chính mình');
  });
});
