import { Injectable } from '@nestjs/common';
import { type Lead as PrismaLead } from '@prisma/client';
import { PrismaService } from '@modules/shared';
import { LeadEntity, type LeadStatus } from '../../domain/entities/lead.entity';
import { type LeadReadDto } from '../../domain/repositories/lead-read.dto';
import { type ILeadRepository, type LeadStatsData, type PaginatedResult } from '../../domain/repositories/lead.repository';
import { LeadScore } from '../../domain/value-objects/lead-score.vo';

@Injectable()
export class PrismaLeadRepository implements ILeadRepository {
  constructor(private readonly prisma: PrismaService) {}

  async findById(id: string): Promise<LeadEntity | null> {
    const lead = await this.prisma.lead.findUnique({ where: { id } });
    return lead ? this.toDomain(lead) : null;
  }

  async save(entity: LeadEntity): Promise<void> {
    await this.prisma.lead.create({
      data: {
        id: entity.id,
        agentId: entity.agentId,
        name: entity.name,
        phone: entity.phone,
        email: entity.email,
        source: entity.source,
        score: entity.score?.value ?? null,
        notes: entity.notes as never,
        status: entity.status,
      },
    });
  }

  async update(entity: LeadEntity): Promise<void> {
    await this.prisma.lead.update({
      where: { id: entity.id },
      data: {
        status: entity.status,
        score: entity.score?.value ?? null,
        notes: entity.notes as never,
      },
    });
  }

  async delete(id: string): Promise<void> {
    await this.prisma.lead.delete({ where: { id } });
  }

  async findByAgent(
    agentId: string,
    status: string | null,
    page: number,
    limit: number,
  ): Promise<PaginatedResult<LeadReadDto>> {
    const take = Math.min(limit, 100);
    const skip = (page - 1) * take;
    const where: Record<string, unknown> = { agentId };
    if (status) {
      where['status'] = status;
    }

    const [data, total] = await Promise.all([
      this.prisma.lead.findMany({
        where,
        skip,
        take,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.lead.count({ where }),
    ]);

    return {
      data: data.map((r) => ({
        id: r.id,
        agentId: r.agentId,
        name: r.name,
        phone: r.phone,
        email: r.email,
        source: r.source,
        score: r.score,
        notes: r.notes,
        status: r.status,
        createdAt: r.createdAt.toISOString(),
        updatedAt: r.updatedAt.toISOString(),
      })),
      total,
      page,
      limit: take,
      totalPages: Math.ceil(total / take),
    };
  }

  async getStatsByAgent(agentId: string): Promise<LeadStatsData> {
    const leads = await this.prisma.lead.findMany({
      where: { agentId },
      select: { status: true, score: true },
    });

    const totalLeads = leads.length;
    const byStatus: Record<string, number> = {};

    let scoreSum = 0;
    let scoreCount = 0;
    let convertedCount = 0;

    for (const lead of leads) {
      byStatus[lead.status] = (byStatus[lead.status] ?? 0) + 1;
      if (lead.score !== null) {
        scoreSum += lead.score;
        scoreCount++;
      }
      if (lead.status === 'CONVERTED') {
        convertedCount++;
      }
    }

    return {
      totalLeads,
      byStatus,
      conversionRate: totalLeads > 0
        ? Math.round((convertedCount / totalLeads) * 10000) / 100
        : 0,
      avgScore: scoreCount > 0
        ? Math.round((scoreSum / scoreCount) * 10) / 10
        : null,
    };
  }

  private toDomain(raw: PrismaLead): LeadEntity {
    let score: LeadScore | null = null;
    if (raw.score !== null) {
      score = LeadScore.create(raw.score).unwrap();
    }

    return new LeadEntity(
      raw.id,
      {
        agentId: raw.agentId,
        name: raw.name,
        phone: raw.phone,
        email: raw.email,
        source: raw.source,
        score,
        notes: raw.notes,
        status: raw.status as LeadStatus,
      },
      raw.createdAt,
      raw.updatedAt,
    );
  }
}
