import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateClientDto } from './dto/create-client.dto';
import { UpdateClientDto } from './dto/update-client.dto';
import { PaginationDto } from '../../common/dto/pagination.dto';

@Injectable()
export class ClientsService {
  constructor(private readonly prisma: PrismaService) {}

  private serializeBigInt(data: any): any {
    return JSON.parse(
      JSON.stringify(data, (_, v) => (typeof v === 'bigint' ? Number(v) : v)),
    );
  }

  async findAll(userId: number, query: PaginationDto) {
    const { page = 1, limit = 20, search, sortBy, sortOrder } = query;
    const skip = (page - 1) * limit;

    const where: any = { userId };

    if (search) {
      where.OR = [
        { name: { contains: search } },
        { email: { contains: search } },
        { phone: { contains: search } },
      ];
    }

    const orderBy: any = {};
    if (sortBy) {
      orderBy[sortBy] = sortOrder || 'asc';
    } else {
      orderBy.createdAt = 'desc';
    }

    const [data, total] = await Promise.all([
      this.prisma.client.findMany({
        where,
        skip,
        take: limit,
        orderBy,
        include: {
          _count: {
            select: { projects: true },
          },
        },
      }),
      this.prisma.client.count({ where }),
    ]);

    return {
      data: this.serializeBigInt(data),
      total,
      page,
      limit,
    };
  }

  async findOne(userId: number, id: number) {
    const client = await this.prisma.client.findFirst({
      where: { id, userId },
      include: {
        _count: {
          select: { projects: true },
        },
      },
    });

    if (!client) {
      throw new NotFoundException(`Client with ID ${id} not found`);
    }

    // Calculate total value of all projects for this client
    const totalValueResult = await this.prisma.project.aggregate({
      where: { clientId: id, userId },
      _sum: { value: true },
    });

    const result = {
      ...client,
      totalProjectValue: totalValueResult._sum.value
        ? Number(totalValueResult._sum.value)
        : 0,
    };

    return this.serializeBigInt(result);
  }

  async create(userId: number, dto: CreateClientDto) {
    const client = await this.prisma.client.create({
      data: {
        ...dto,
        userId,
      },
    });

    return this.serializeBigInt(client);
  }

  async update(userId: number, id: number, dto: UpdateClientDto) {
    const existing = await this.prisma.client.findFirst({
      where: { id, userId },
    });

    if (!existing) {
      throw new NotFoundException(`Client with ID ${id} not found`);
    }

    const updated = await this.prisma.client.update({
      where: { id },
      data: dto,
    });

    return this.serializeBigInt(updated);
  }

  async remove(userId: number, id: number) {
    const existing = await this.prisma.client.findFirst({
      where: { id, userId },
    });

    if (!existing) {
      throw new NotFoundException(`Client with ID ${id} not found`);
    }

    await this.prisma.client.delete({ where: { id } });

    return { message: `Client with ID ${id} has been deleted` };
  }

  async getSummary(userId: number, id: number) {
    const client = await this.prisma.client.findFirst({
      where: { id, userId },
    });

    if (!client) {
      throw new NotFoundException(`Client with ID ${id} not found`);
    }

    const projectCount = await this.prisma.project.count({
      where: { clientId: id, userId },
    });

    const totalValueResult = await this.prisma.project.aggregate({
      where: { clientId: id, userId },
      _sum: { value: true },
    });

    const recentProjects = await this.prisma.project.findMany({
      where: { clientId: id, userId },
      orderBy: { createdAt: 'desc' },
      take: 5,
      select: {
        id: true,
        name: true,
        nameEn: true,
        status: true,
        value: true,
        startDate: true,
        endDate: true,
        createdAt: true,
      },
    });

    return this.serializeBigInt({
      projectCount,
      totalValue: totalValueResult._sum.value
        ? Number(totalValueResult._sum.value)
        : 0,
      recentProjects,
    });
  }
}
