import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ListingEntity } from '../../domain/entities/listing.entity';
import { Price } from '../../domain/value-objects/price.vo';
import { PrismaListingRepository } from '../repositories/prisma-listing.repository';

describe('PrismaListingRepository', () => {
  let repository: PrismaListingRepository;
  let mockPrisma: {
    listing: {
      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 = {
      listing: {
        findUnique: vi.fn(),
        create: vi.fn().mockResolvedValue(undefined),
        update: vi.fn().mockResolvedValue(undefined),
        findMany: vi.fn().mockResolvedValue([]),
        count: vi.fn().mockResolvedValue(0),
      },
    };
    repository = new PrismaListingRepository(mockPrisma as any);
  });

  describe('findById', () => {
    it('should return null when listing not found', async () => {
      mockPrisma.listing.findUnique.mockResolvedValue(null);

      const result = await repository.findById('non-existent');

      expect(result).toBeNull();
      expect(mockPrisma.listing.findUnique).toHaveBeenCalledWith({
        where: { id: 'non-existent' },
      });
    });

    it('should return a ListingEntity when listing is found', async () => {
      const now = new Date();
      mockPrisma.listing.findUnique.mockResolvedValue({
        id: 'listing-1',
        propertyId: 'prop-1',
        agentId: 'agent-1',
        sellerId: 'seller-1',
        transactionType: 'SALE',
        status: 'ACTIVE',
        priceVND: 5_000_000_000n,
        pricePerM2: 62_500_000,
        rentPriceMonthly: null,
        commissionPct: 2.0,
        aiPriceEstimate: null,
        aiConfidence: null,
        moderationScore: null,
        moderationNotes: null,
        viewCount: 10,
        saveCount: 2,
        inquiryCount: 1,
        featuredUntil: null,
        expiresAt: null,
        publishedAt: now,
        createdAt: now,
        updatedAt: now,
      });

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

      expect(result).toBeInstanceOf(ListingEntity);
      expect(result!.id).toBe('listing-1');
      expect(result!.propertyId).toBe('prop-1');
      expect(result!.status).toBe('ACTIVE');
      expect(result!.viewCount).toBe(10);
    });
  });

  describe('save', () => {
    it('should call prisma.listing.create with correct data', async () => {
      const price = Price.create(5_000_000_000n).unwrap();
      const listing = ListingEntity.createNew(
        'listing-new',
        'prop-new',
        'seller-1',
        'SALE',
        price,
        100,
        'agent-1',
      );

      await repository.save(listing);

      expect(mockPrisma.listing.create).toHaveBeenCalledTimes(1);
      const createCall = mockPrisma.listing.create.mock.calls[0]![0];
      expect(createCall.data.id).toBe('listing-new');
      expect(createCall.data.propertyId).toBe('prop-new');
      expect(createCall.data.sellerId).toBe('seller-1');
      expect(createCall.data.status).toBe('DRAFT');
    });
  });

  describe('update', () => {
    it('should call prisma.listing.update with correct data', async () => {
      const price = Price.create(5_000_000_000n).unwrap();
      const listing = ListingEntity.createNew(
        'listing-upd',
        'prop-upd',
        'seller-1',
        'SALE',
        price,
        80,
      );

      await repository.update(listing);

      expect(mockPrisma.listing.update).toHaveBeenCalledTimes(1);
      const updateCall = mockPrisma.listing.update.mock.calls[0]![0];
      expect(updateCall.where.id).toBe('listing-upd');
      expect(updateCall.data.status).toBe('DRAFT');
    });
  });

  describe('findByStatus', () => {
    it('should delegate to search with status filter', async () => {
      mockPrisma.listing.findMany.mockResolvedValue([]);
      mockPrisma.listing.count.mockResolvedValue(0);

      const result = await repository.findByStatus('PENDING_REVIEW', 1, 20);

      expect(result).toEqual({
        data: [],
        total: 0,
        page: 1,
        limit: 20,
        totalPages: 0,
      });
    });
  });
});
