import { type IReviewRepository } from '../../domain/repositories/review.repository';
import { GetReviewsByUserHandler } from '../queries/get-reviews-by-user/get-reviews-by-user.handler';
import { GetReviewsByUserQuery } from '../queries/get-reviews-by-user/get-reviews-by-user.query';

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

  const mockPaginatedResult = {
    data: [
      {
        id: 'review-1',
        userId: 'user-1',
        userName: 'Nguyen Van A',
        targetType: 'agent',
        targetId: 'agent-1',
        rating: 5,
        comment: 'Very good',
        createdAt: '2026-01-01T00:00:00.000Z',
      },
    ],
    total: 1,
    page: 1,
    limit: 20,
    totalPages: 1,
  };

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

    handler = new GetReviewsByUserHandler(mockReviewRepo as any);
  });

  it('returns paginated reviews for a user', async () => {
    mockReviewRepo.findByUserId.mockResolvedValue(mockPaginatedResult);

    const query = new GetReviewsByUserQuery('user-1', 1, 20);
    const result = await handler.execute(query);

    expect(result).toEqual(mockPaginatedResult);
    expect(mockReviewRepo.findByUserId).toHaveBeenCalledWith('user-1', 1, 20);
  });

  it('returns empty results when user has no reviews', async () => {
    const emptyResult = {
      data: [],
      total: 0,
      page: 1,
      limit: 20,
      totalPages: 0,
    };
    mockReviewRepo.findByUserId.mockResolvedValue(emptyResult);

    const query = new GetReviewsByUserQuery('user-no-reviews', 1, 20);
    const result = await handler.execute(query);

    expect(result.data).toHaveLength(0);
    expect(result.total).toBe(0);
    expect(result.totalPages).toBe(0);
    expect(mockReviewRepo.findByUserId).toHaveBeenCalledWith('user-no-reviews', 1, 20);
  });

  it('passes custom pagination params', async () => {
    mockReviewRepo.findByUserId.mockResolvedValue({
      ...mockPaginatedResult,
      page: 3,
      limit: 5,
    });

    const query = new GetReviewsByUserQuery('user-1', 3, 5);
    await handler.execute(query);

    expect(mockReviewRepo.findByUserId).toHaveBeenCalledWith('user-1', 3, 5);
  });

  it('returns multiple reviews across pages', async () => {
    const multiPageResult = {
      data: [
        {
          id: 'review-1',
          userId: 'user-1',
          userName: 'Nguyen Van A',
          targetType: 'agent',
          targetId: 'agent-1',
          rating: 5,
          comment: 'Excellent',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
        {
          id: 'review-2',
          userId: 'user-1',
          userName: 'Nguyen Van A',
          targetType: 'property',
          targetId: 'prop-1',
          rating: 3,
          comment: null,
          createdAt: '2026-01-02T00:00:00.000Z',
        },
      ],
      total: 15,
      page: 1,
      limit: 2,
      totalPages: 8,
    };
    mockReviewRepo.findByUserId.mockResolvedValue(multiPageResult);

    const query = new GetReviewsByUserQuery('user-1', 1, 2);
    const result = await handler.execute(query);

    expect(result.data).toHaveLength(2);
    expect(result.total).toBe(15);
    expect(result.totalPages).toBe(8);
  });
});
