import { AgentEntity } from '../../domain/entities/agent.entity';
import { QualityScore } from '../../domain/value-objects/quality-score.vo';
import { PrismaAgentRepository } from '../repositories/prisma-agent.repository';

describe('PrismaAgentRepository', () => {
  let repository: PrismaAgentRepository;
  let mockPrisma: {
    agent: {
      findUnique: ReturnType<typeof vi.fn>;
      findUniqueOrThrow: ReturnType<typeof vi.fn>;
      update: ReturnType<typeof vi.fn>;
    };
    lead: {
      groupBy: ReturnType<typeof vi.fn>;
      count: ReturnType<typeof vi.fn>;
    };
    inquiry: {
      count: ReturnType<typeof vi.fn>;
    };
    listing: {
      count: ReturnType<typeof vi.fn>;
      findMany: ReturnType<typeof vi.fn>;
    };
    review: {
      aggregate: ReturnType<typeof vi.fn>;
    };
  };

  const agentRow = {
    id: 'agent-1',
    userId: 'user-1',
    licenseNumber: 'BDS-001',
    agency: 'Công ty BĐS',
    qualityScore: 85,
    totalDeals: 12,
    responseTimeAvg: 600,
    bio: 'Chuyên viên BĐS',
    serviceAreas: ['Quận 7'],
    isVerified: true,
    createdAt: new Date('2024-01-01'),
    updatedAt: new Date('2024-06-01'),
  };

  beforeEach(() => {
    mockPrisma = {
      agent: {
        findUnique: vi.fn(),
        findUniqueOrThrow: vi.fn(),
        update: vi.fn(),
      },
      lead: {
        groupBy: vi.fn(),
        count: vi.fn(),
      },
      inquiry: {
        count: vi.fn(),
      },
      listing: {
        count: vi.fn(),
        findMany: vi.fn(),
      },
      review: {
        aggregate: vi.fn(),
      },
    };
    repository = new PrismaAgentRepository(mockPrisma as any);
  });

  describe('findByUserId', () => {
    it('returns AgentEntity when found', async () => {
      mockPrisma.agent.findUnique.mockResolvedValue(agentRow);

      const result = await repository.findByUserId('user-1');

      expect(result).toBeInstanceOf(AgentEntity);
      expect(result!.id).toBe('agent-1');
      expect(result!.userId).toBe('user-1');
      expect(result!.qualityScore.value).toBe(85);
    });

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

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

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

  describe('findById', () => {
    it('returns AgentEntity when found', async () => {
      mockPrisma.agent.findUnique.mockResolvedValue(agentRow);

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

      expect(result).toBeInstanceOf(AgentEntity);
      expect(result!.id).toBe('agent-1');
      expect(result!.qualityScore.value).toBe(85);
    });

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

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

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

  describe('save', () => {
    it('updates the quality score for the agent', async () => {
      mockPrisma.agent.update.mockResolvedValue(undefined);

      const agent = new AgentEntity('agent-1', {
        userId: 'user-1',
        licenseNumber: null,
        agency: null,
        qualityScore: QualityScore.fromPersistence(92),
        totalDeals: 0,
        responseTimeAvg: null,
        bio: null,
        serviceAreas: [],
        isVerified: false,
      });

      await repository.save(agent);

      expect(mockPrisma.agent.update).toHaveBeenCalledWith({
        where: { id: 'agent-1' },
        data: { qualityScore: 92 },
      });
    });

    it('handles score of 0', async () => {
      mockPrisma.agent.update.mockResolvedValue(undefined);

      const agent = new AgentEntity('agent-1', {
        userId: 'user-1',
        licenseNumber: null,
        agency: null,
        qualityScore: QualityScore.fromPersistence(0),
        totalDeals: 0,
        responseTimeAvg: null,
        bio: null,
        serviceAreas: [],
        isVerified: false,
      });

      await repository.save(agent);

      expect(mockPrisma.agent.update).toHaveBeenCalledWith({
        where: { id: 'agent-1' },
        data: { qualityScore: 0 },
      });
    });
  });

  describe('getQualityScoreInputs', () => {
    it('returns quality score input data', async () => {
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: 4.5 },
        _count: { rating: 10 },
      });
      mockPrisma.lead.count
        .mockResolvedValueOnce(20) // totalLeads
        .mockResolvedValueOnce(5);  // convertedLeads
      mockPrisma.listing.count
        .mockResolvedValueOnce(10) // totalListings
        .mockResolvedValueOnce(7);  // activeListings
      mockPrisma.agent.findUnique.mockResolvedValue({ responseTimeAvg: 900 });

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

      expect(result.avgRating).toBe(4.5);
      expect(result.totalReviews).toBe(10);
      expect(result.responseTimeAvg).toBe(900);
      expect(result.conversionRate).toBe(0.25);
      expect(result.activeListingRatio).toBe(0.7);
    });

    it('handles zero counts gracefully', async () => {
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: null },
        _count: { rating: 0 },
      });
      mockPrisma.lead.count.mockResolvedValue(0);
      mockPrisma.listing.count.mockResolvedValue(0);
      mockPrisma.agent.findUnique.mockResolvedValue({ responseTimeAvg: null });

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

      expect(result.avgRating).toBe(0);
      expect(result.totalReviews).toBe(0);
      expect(result.responseTimeAvg).toBeNull();
      expect(result.conversionRate).toBe(0);
      expect(result.activeListingRatio).toBe(0);
    });
  });

  describe('getDashboard', () => {
    it('returns full dashboard data', async () => {
      mockPrisma.agent.findUniqueOrThrow.mockResolvedValue({
        id: 'agent-1',
        qualityScore: 85,
        totalDeals: 12,
        responseTimeAvg: 600,
        isVerified: true,
      });
      mockPrisma.lead.groupBy.mockResolvedValue([
        { status: 'NEW', _count: { id: 5 } },
        { status: 'CONTACTED', _count: { id: 10 } },
        { status: 'CONVERTED', _count: { id: 3 } },
      ]);
      mockPrisma.inquiry.count
        .mockResolvedValueOnce(45) // total
        .mockResolvedValueOnce(3); // unread
      mockPrisma.listing.count
        .mockResolvedValueOnce(15) // total
        .mockResolvedValueOnce(10); // active
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: 4.5 },
        _count: { rating: 20 },
      });

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

      expect(result.agentId).toBe('agent-1');
      expect(result.qualityScore).toBe(85);
      expect(result.totalDeals).toBe(12);
      expect(result.responseTimeAvg).toBe(600);
      expect(result.isVerified).toBe(true);
      expect(result.totalLeads).toBe(18);
      expect(result.leadsByStatus).toEqual({ NEW: 5, CONTACTED: 10, CONVERTED: 3 });
      expect(result.conversionRate).toBe(0.167);
      expect(result.totalInquiries).toBe(45);
      expect(result.unreadInquiries).toBe(3);
      expect(result.totalListings).toBe(15);
      expect(result.activeListings).toBe(10);
      expect(result.avgReviewRating).toBe(4.5);
      expect(result.totalReviews).toBe(20);
    });

    it('handles agent with zero leads', async () => {
      mockPrisma.agent.findUniqueOrThrow.mockResolvedValue({
        id: 'agent-1',
        qualityScore: 0,
        totalDeals: 0,
        responseTimeAvg: null,
        isVerified: false,
      });
      mockPrisma.lead.groupBy.mockResolvedValue([]);
      mockPrisma.inquiry.count.mockResolvedValue(0);
      mockPrisma.listing.count.mockResolvedValue(0);
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: null },
        _count: { rating: 0 },
      });

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

      expect(result.totalLeads).toBe(0);
      expect(result.conversionRate).toBe(0);
      expect(result.leadsByStatus).toEqual({});
      expect(result.avgReviewRating).toBe(0);
      expect(result.totalReviews).toBe(0);
    });
  });

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

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

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

    it('returns full public profile', async () => {
      const now = new Date();
      mockPrisma.agent.findUnique.mockResolvedValue({
        id: 'agent-1',
        agency: 'Công ty BĐS ABC',
        licenseNumber: 'BDS-001',
        bio: 'Chuyên viên BĐS',
        qualityScore: 85,
        totalDeals: 50,
        isVerified: true,
        serviceAreas: ['Quận 7', 'Quận 2'],
        createdAt: now,
        user: {
          fullName: 'Nguyễn Văn A',
          avatarUrl: 'https://example.com/avatar.jpg',
          phone: '0901234567',
          email: 'agent@example.com',
          createdAt: now,
        },
      });
      mockPrisma.listing.findMany.mockResolvedValue([]);
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: 4.5 },
        _count: { rating: 20 },
      });

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

      expect(result).not.toBeNull();
      expect(result!.id).toBe('agent-1');
      expect(result!.fullName).toBe('Nguyễn Văn A');
      expect(result!.agency).toBe('Công ty BĐS ABC');
      expect(result!.qualityScore).toBe(85);
      expect(result!.serviceAreas).toEqual(['Quận 7', 'Quận 2']);
      expect(result!.avgReviewRating).toBe(4.5);
      expect(result!.totalReviews).toBe(20);
      expect(result!.activeListings).toEqual([]);
    });

    it('returns profile with active listings including property data', async () => {
      const now = new Date();
      mockPrisma.agent.findUnique.mockResolvedValue({
        id: 'agent-1',
        agency: null,
        licenseNumber: null,
        bio: null,
        qualityScore: 70,
        totalDeals: 5,
        isVerified: true,
        serviceAreas: [],
        createdAt: now,
        user: {
          fullName: 'Lê Văn C',
          avatarUrl: null,
          phone: '0903456789',
          email: null,
          createdAt: now,
        },
      });
      mockPrisma.listing.findMany.mockResolvedValue([
        {
          id: 'listing-1',
          transactionType: 'SALE',
          priceVND: BigInt('5000000000'),
          status: 'ACTIVE',
          property: {
            id: 'prop-1',
            title: 'Căn hộ cao cấp',
            propertyType: 'APARTMENT',
            address: '123 Nguyễn Hữu Thọ',
            district: 'Quận 7',
            city: 'TP.HCM',
            areaM2: 75,
            bedrooms: 2,
            bathrooms: 2,
            media: [{ url: 'https://example.com/image.jpg' }],
          },
        },
      ]);
      mockPrisma.review.aggregate.mockResolvedValue({
        _avg: { rating: 3.8 },
        _count: { rating: 5 },
      });

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

      expect(result!.activeListings).toHaveLength(1);
      const listing = result!.activeListings[0]!;
      expect(listing.id).toBe('listing-1');
      expect(listing.transactionType).toBe('SALE');
      expect(listing.priceVND).toBe('5000000000');
      expect(listing.property.title).toBe('Căn hộ cao cấp');
      expect(listing.property.imageUrl).toBe('https://example.com/image.jpg');
    });
  });
});
