import { describe, it, expect, vi, beforeEach } from 'vitest';
import { PropertyMediaEntity } from '../../domain/entities/property-media.entity';
import { PropertyEntity } from '../../domain/entities/property.entity';
import { PrismaPropertyRepository } from '../repositories/prisma-property.repository';

describe('PrismaPropertyRepository', () => {
  let repository: PrismaPropertyRepository;
  let mockPrisma: {
    property: {
      findUnique: ReturnType<typeof vi.fn>;
    };
    propertyMedia: {
      create: ReturnType<typeof vi.fn>;
      findMany: ReturnType<typeof vi.fn>;
      delete: ReturnType<typeof vi.fn>;
      count: ReturnType<typeof vi.fn>;
    };
    $executeRaw: ReturnType<typeof vi.fn>;
  };

  beforeEach(() => {
    mockPrisma = {
      property: {
        findUnique: vi.fn(),
      },
      propertyMedia: {
        create: vi.fn().mockResolvedValue(undefined),
        findMany: vi.fn().mockResolvedValue([]),
        delete: vi.fn().mockResolvedValue(undefined),
        count: vi.fn().mockResolvedValue(0),
      },
      $executeRaw: vi.fn().mockResolvedValue(1),
    };
    repository = new PrismaPropertyRepository(mockPrisma as any);
  });

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

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

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

    it('should return a PropertyEntity when property is found', async () => {
      const now = new Date();
      mockPrisma.property.findUnique.mockResolvedValue({
        id: 'prop-1',
        propertyType: 'APARTMENT',
        title: 'Căn hộ đẹp',
        description: 'Mô tả chi tiết',
        address: '123 Nguyễn Huệ',
        ward: 'Bến Nghé',
        district: 'Quận 1',
        city: 'Hồ Chí Minh',
        location: null, // PostGIS geometry placeholder
        areaM2: 80,
        usableAreaM2: 70,
        bedrooms: 2,
        bathrooms: 2,
        floors: null,
        floor: 10,
        totalFloors: 25,
        direction: 'EAST',
        yearBuilt: 2022,
        legalStatus: 'Sổ hồng',
        amenities: null,
        nearbyPOIs: null,
        metroDistanceM: 300,
        projectName: 'Vinhomes',
        createdAt: now,
        updatedAt: now,
      });

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

      expect(result).toBeInstanceOf(PropertyEntity);
      expect(result!.id).toBe('prop-1');
      expect(result!.title).toBe('Căn hộ đẹp');
      expect(result!.propertyType).toBe('APARTMENT');
    });
  });

  describe('addMedia', () => {
    it('should call prisma.propertyMedia.create with correct data', async () => {
      const media = PropertyMediaEntity.createNew(
        'media-1',
        'prop-1',
        'https://cdn.example.com/photo.jpg',
        'image',
        0,
        'Mặt tiền',
      );

      await repository.addMedia(media);

      expect(mockPrisma.propertyMedia.create).toHaveBeenCalledTimes(1);
      const createCall = mockPrisma.propertyMedia.create.mock.calls[0]![0];
      expect(createCall.data.id).toBe('media-1');
      expect(createCall.data.propertyId).toBe('prop-1');
      expect(createCall.data.url).toBe('https://cdn.example.com/photo.jpg');
      expect(createCall.data.type).toBe('image');
      expect(createCall.data.order).toBe(0);
      expect(createCall.data.caption).toBe('Mặt tiền');
    });
  });

  describe('findMediaByPropertyId', () => {
    it('should return mapped PropertyMediaEntity array', async () => {
      const now = new Date();
      mockPrisma.propertyMedia.findMany.mockResolvedValue([
        {
          id: 'media-1',
          propertyId: 'prop-1',
          url: 'https://cdn.example.com/1.jpg',
          type: 'image',
          order: 0,
          caption: null,
          aiTags: null,
          createdAt: now,
        },
        {
          id: 'media-2',
          propertyId: 'prop-1',
          url: 'https://cdn.example.com/2.jpg',
          type: 'image',
          order: 1,
          caption: 'Phòng ngủ',
          aiTags: null,
          createdAt: now,
        },
      ]);

      const result = await repository.findMediaByPropertyId('prop-1');

      expect(result).toHaveLength(2);
      expect(result[0]).toBeInstanceOf(PropertyMediaEntity);
      expect(result[0]!.id).toBe('media-1');
      expect(result[1]!.caption).toBe('Phòng ngủ');
    });
  });

  describe('deleteMedia', () => {
    it('should call prisma.propertyMedia.delete', async () => {
      await repository.deleteMedia('media-1');

      expect(mockPrisma.propertyMedia.delete).toHaveBeenCalledWith({
        where: { id: 'media-1' },
      });
    });
  });

  describe('countMediaByPropertyId', () => {
    it('should return count from prisma', async () => {
      mockPrisma.propertyMedia.count.mockResolvedValue(5);

      const result = await repository.countMediaByPropertyId('prop-1');

      expect(result).toBe(5);
      expect(mockPrisma.propertyMedia.count).toHaveBeenCalledWith({
        where: { propertyId: 'prop-1' },
      });
    });
  });
});
