import { Injectable } from '@nestjs/common';
import { PrismaService } from '@modules/shared';
import { AgentEntity } from '../../domain/entities/agent.entity';
import {
  type AgentDashboardData,
  type AgentPublicProfileData,
  type IAgentRepository,
  type QualityScoreInputData,
} from '../../domain/repositories/agent.repository';
import { QualityScore } from '../../domain/value-objects/quality-score.vo';
import { buildPublicProfile } from './agent-profile.queries';

@Injectable()
export class PrismaAgentRepository implements IAgentRepository {
  constructor(private readonly prisma: PrismaService) {}

  async findByUserId(userId: string): Promise<AgentEntity | null> {
    const row = await this.prisma.agent.findUnique({ where: { userId } });
    if (!row) return null;
    return this.toDomain(row);
  }

  async findById(agentId: string): Promise<AgentEntity | null> {
    const row = await this.prisma.agent.findUnique({ where: { id: agentId } });
    if (!row) return null;
    return this.toDomain(row);
  }

  async save(agent: AgentEntity): Promise<void> {
    await this.prisma.agent.update({
      where: { id: agent.id },
      data: { qualityScore: agent.qualityScore.value },
    });
  }

  async getDashboard(agentId: string): Promise<AgentDashboardData> {
    const [agent, leads, inquiryStats, listingStats, reviewStats] =
      await Promise.all([
        this.prisma.agent.findUniqueOrThrow({
          where: { id: agentId },
          select: {
            id: true, qualityScore: true, totalDeals: true,
            responseTimeAvg: true, isVerified: true,
          },
        }),
        this.prisma.lead.groupBy({
          by: ['status'],
          where: { agentId },
          _count: { id: true },
        }),
        this.getInquiryStats(agentId),
        this.getListingStats(agentId),
        this.prisma.review.aggregate({
          where: { targetType: 'AGENT', targetId: agentId },
          _avg: { rating: true },
          _count: { rating: true },
        }),
      ]);

    const leadsByStatus: Record<string, number> = {};
    let totalLeads = 0;
    let convertedLeads = 0;

    for (const group of leads) {
      leadsByStatus[group.status] = group._count.id;
      totalLeads += group._count.id;
      if (group.status === 'CONVERTED') convertedLeads = group._count.id;
    }

    const conversionRate = totalLeads > 0 ? convertedLeads / totalLeads : 0;

    return {
      agentId: agent.id,
      qualityScore: agent.qualityScore,
      totalDeals: agent.totalDeals,
      responseTimeAvg: agent.responseTimeAvg,
      isVerified: agent.isVerified,
      totalLeads,
      leadsByStatus,
      conversionRate: Math.round(conversionRate * 1000) / 1000,
      totalInquiries: inquiryStats.total,
      unreadInquiries: inquiryStats.unread,
      totalListings: listingStats.total,
      activeListings: listingStats.active,
      avgReviewRating: Math.round((reviewStats._avg.rating ?? 0) * 10) / 10,
      totalReviews: reviewStats._count.rating,
    };
  }

  async getPublicProfile(agentId: string): Promise<AgentPublicProfileData | null> {
    return buildPublicProfile(this.prisma, agentId);
  }

  async getQualityScoreInputs(agentId: string): Promise<QualityScoreInputData> {
    const [reviewStats, leadCounts, listingCounts, agentRecord] =
      await Promise.all([
        this.prisma.review.aggregate({
          where: { targetType: 'AGENT', targetId: agentId },
          _avg: { rating: true },
          _count: { rating: true },
        }),
        Promise.all([
          this.prisma.lead.count({ where: { agentId } }),
          this.prisma.lead.count({ where: { agentId, status: 'CONVERTED' } }),
        ]),
        Promise.all([
          this.prisma.listing.count({ where: { agentId } }),
          this.prisma.listing.count({ where: { agentId, status: 'ACTIVE' } }),
        ]),
        this.prisma.agent.findUnique({
          where: { id: agentId },
          select: { responseTimeAvg: true },
        }),
      ]);

    const [totalLeads, convertedLeads] = leadCounts;
    const [totalListings, activeListings] = listingCounts;

    return {
      avgRating: reviewStats._avg.rating ?? 0,
      totalReviews: reviewStats._count.rating,
      responseTimeAvg: agentRecord?.responseTimeAvg ?? null,
      conversionRate: totalLeads > 0 ? convertedLeads / totalLeads : 0,
      activeListingRatio: totalListings > 0 ? activeListings / totalListings : 0,
    };
  }

  private async getInquiryStats(
    agentId: string,
  ): Promise<{ total: number; unread: number }> {
    const [total, unread] = await Promise.all([
      this.prisma.inquiry.count({ where: { listing: { agentId } } }),
      this.prisma.inquiry.count({ where: { listing: { agentId }, isRead: false } }),
    ]);
    return { total, unread };
  }

  private async getListingStats(
    agentId: string,
  ): Promise<{ total: number; active: number }> {
    const [total, active] = await Promise.all([
      this.prisma.listing.count({ where: { agentId } }),
      this.prisma.listing.count({ where: { agentId, status: 'ACTIVE' } }),
    ]);
    return { total, active };
  }

  private toDomain(row: {
    id: string;
    userId: string;
    licenseNumber: string | null;
    agency: string | null;
    qualityScore: number;
    totalDeals: number;
    responseTimeAvg: number | null;
    bio: string | null;
    serviceAreas: unknown;
    isVerified: boolean;
    createdAt: Date;
    updatedAt: Date;
  }): AgentEntity {
    return new AgentEntity(
      row.id,
      {
        userId: row.userId,
        licenseNumber: row.licenseNumber,
        agency: row.agency,
        qualityScore: QualityScore.fromPersistence(row.qualityScore),
        totalDeals: row.totalDeals,
        responseTimeAvg: row.responseTimeAvg,
        bio: row.bio,
        serviceAreas: (row.serviceAreas as string[]) ?? [],
        isVerified: row.isVerified,
      },
      row.createdAt,
      row.updatedAt,
    );
  }
}
