import { InquiryEntity } from '../../domain/entities/inquiry.entity';
import { PrismaInquiryRepository } from '../repositories/prisma-inquiry.repository';

describe('PrismaInquiryRepository', () => {
  let repository: PrismaInquiryRepository;
  let mockPrisma: {
    inquiry: {
      findUnique: ReturnType<typeof vi.fn>;
      create: ReturnType<typeof vi.fn>;
      update: ReturnType<typeof vi.fn>;
      findMany: ReturnType<typeof vi.fn>;
      count: ReturnType<typeof vi.fn>;
    };
  };

  beforeEach(() => {
    mockPrisma = {
      inquiry: {
        findUnique: vi.fn(),
        create: vi.fn(),
        update: vi.fn(),
        findMany: vi.fn(),
        count: vi.fn(),
      },
    };
    repository = new PrismaInquiryRepository(mockPrisma as any);
  });

  describe('findById', () => {
    it('returns an InquiryEntity when found', async () => {
      const now = new Date();
      mockPrisma.inquiry.findUnique.mockResolvedValue({
        id: 'inq-1',
        listingId: 'listing-1',
        userId: 'user-1',
        message: 'Tôi muốn xem nhà',
        phone: '0901234567',
        isRead: false,
        createdAt: now,
      });

      const result = await repository.findById('inq-1');

      expect(result).toBeInstanceOf(InquiryEntity);
      expect(result!.id).toBe('inq-1');
      expect(result!.listingId).toBe('listing-1');
      expect(result!.userId).toBe('user-1');
      expect(result!.message).toBe('Tôi muốn xem nhà');
      expect(result!.phone).toBe('0901234567');
      expect(result!.isRead).toBe(false);
    });

    it('returns null when not found', async () => {
      mockPrisma.inquiry.findUnique.mockResolvedValue(null);

      const result = await repository.findById('nonexistent');

      expect(result).toBeNull();
    });
  });

  describe('save', () => {
    it('creates a Prisma record from the entity', async () => {
      mockPrisma.inquiry.create.mockResolvedValue(undefined);
      const entity = InquiryEntity.createNew(
        'inq-1',
        'listing-1',
        'user-1',
        'Tôi muốn xem nhà',
        '0901234567',
      );

      await repository.save(entity);

      expect(mockPrisma.inquiry.create).toHaveBeenCalledWith({
        data: {
          id: 'inq-1',
          listingId: 'listing-1',
          userId: 'user-1',
          message: 'Tôi muốn xem nhà',
          phone: '0901234567',
          isRead: false,
        },
      });
    });

    it('saves entity with null phone', async () => {
      mockPrisma.inquiry.create.mockResolvedValue(undefined);
      const entity = InquiryEntity.createNew(
        'inq-2',
        'listing-1',
        'user-1',
        'Cho tôi hỏi giá',
        null,
      );

      await repository.save(entity);

      const callData = mockPrisma.inquiry.create.mock.calls[0]![0].data;
      expect(callData.phone).toBeNull();
    });
  });

  describe('markAsRead', () => {
    it('updates the isRead field to true', async () => {
      mockPrisma.inquiry.update.mockResolvedValue(undefined);

      await repository.markAsRead('inq-1');

      expect(mockPrisma.inquiry.update).toHaveBeenCalledWith({
        where: { id: 'inq-1' },
        data: { isRead: true },
      });
    });
  });

  describe('findByListing', () => {
    it('returns paginated results', async () => {
      const now = new Date();
      mockPrisma.inquiry.findMany.mockResolvedValue([
        {
          id: 'inq-1',
          listingId: 'listing-1',
          userId: 'user-1',
          message: 'Xin chào',
          phone: null,
          isRead: false,
          createdAt: now,
          listing: { id: 'listing-1', property: { title: 'Căn hộ Quận 1' } },
          user: { id: 'user-1', fullName: 'Nguyễn Văn A', phone: '0901234567' },
        },
      ]);
      mockPrisma.inquiry.count.mockResolvedValue(1);

      const result = await repository.findByListing('listing-1', 1, 20);

      expect(result.data).toHaveLength(1);
      expect(result.total).toBe(1);
      expect(result.page).toBe(1);
      expect(result.limit).toBe(20);
      expect(result.totalPages).toBe(1);
      expect(result.data[0]!.listingTitle).toBe('Căn hộ Quận 1');
      expect(result.data[0]!.userName).toBe('Nguyễn Văn A');
    });

    it('caps limit at 100', async () => {
      mockPrisma.inquiry.findMany.mockResolvedValue([]);
      mockPrisma.inquiry.count.mockResolvedValue(0);

      const result = await repository.findByListing('listing-1', 1, 200);

      expect(result.limit).toBe(100);
      expect(mockPrisma.inquiry.findMany).toHaveBeenCalledWith(
        expect.objectContaining({ take: 100 }),
      );
    });

    it('calculates correct skip for pagination', async () => {
      mockPrisma.inquiry.findMany.mockResolvedValue([]);
      mockPrisma.inquiry.count.mockResolvedValue(0);

      await repository.findByListing('listing-1', 3, 10);

      expect(mockPrisma.inquiry.findMany).toHaveBeenCalledWith(
        expect.objectContaining({ skip: 20 }),
      );
    });
  });

  describe('findByAgent', () => {
    it('returns paginated results for agent', async () => {
      const now = new Date();
      mockPrisma.inquiry.findMany.mockResolvedValue([
        {
          id: 'inq-1',
          listingId: 'listing-1',
          userId: 'user-1',
          message: 'Xin chào',
          phone: null,
          isRead: true,
          createdAt: now,
          listing: { id: 'listing-1', property: { title: 'Nhà phố Quận 7' } },
          user: { id: 'user-1', fullName: 'Trần Thị B', phone: '0902345678' },
        },
      ]);
      mockPrisma.inquiry.count.mockResolvedValue(1);

      const result = await repository.findByAgent('agent-1', 1, 20);

      expect(result.data).toHaveLength(1);
      expect(result.data[0]!.listingTitle).toBe('Nhà phố Quận 7');
      expect(result.data[0]!.userName).toBe('Trần Thị B');
      expect(result.data[0]!.isRead).toBe(true);
    });

    it('queries using listing.agentId filter', async () => {
      mockPrisma.inquiry.findMany.mockResolvedValue([]);
      mockPrisma.inquiry.count.mockResolvedValue(0);

      await repository.findByAgent('agent-1', 1, 20);

      expect(mockPrisma.inquiry.findMany).toHaveBeenCalledWith(
        expect.objectContaining({
          where: { listing: { agentId: 'agent-1' } },
        }),
      );
    });
  });

  describe('countUnreadByAgent', () => {
    it('returns the count of unread inquiries for the agent', async () => {
      mockPrisma.inquiry.count.mockResolvedValue(5);

      const result = await repository.countUnreadByAgent('agent-1');

      expect(result).toBe(5);
      expect(mockPrisma.inquiry.count).toHaveBeenCalledWith({
        where: {
          isRead: false,
          listing: { agentId: 'agent-1' },
        },
      });
    });

    it('returns 0 when no unread inquiries exist', async () => {
      mockPrisma.inquiry.count.mockResolvedValue(0);

      const result = await repository.countUnreadByAgent('agent-1');

      expect(result).toBe(0);
    });
  });
});
