import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateProjectDto } from './dto/create-project.dto';
import { UpdateProjectDto } from './dto/update-project.dto';
import { PaginationDto } from '../../common/dto/pagination.dto';

@Injectable()
export class ProjectsService {
  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 & { status?: string }) {
    const { page = 1, limit = 20, search, sortBy, sortOrder, status } = query;
    const skip = (page - 1) * limit;

    const where: any = { userId };

    if (status) {
      where.status = status;
    }

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

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

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

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

  async findOne(userId: number, id: number) {
    const project = await this.prisma.project.findFirst({
      where: { id, userId },
      include: {
        client: true,
        estimates: {
          orderBy: { createdAt: 'desc' },
        },
        contracts: {
          orderBy: { createdAt: 'desc' },
        },
        settlements: {
          orderBy: { createdAt: 'desc' },
        },
      },
    });

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

    return this.serializeBigInt(project);
  }

  async create(userId: number, dto: CreateProjectDto) {
    const data: any = {
      name: dto.name,
      userId,
    };

    if (dto.nameEn !== undefined) data.nameEn = dto.nameEn;
    if (dto.clientId !== undefined) data.clientId = dto.clientId;
    if (dto.status !== undefined) data.status = dto.status;
    if (dto.value !== undefined) data.value = BigInt(dto.value);
    if (dto.startDate !== undefined) data.startDate = new Date(dto.startDate);
    if (dto.endDate !== undefined) data.endDate = new Date(dto.endDate);
    if (dto.description !== undefined) data.description = dto.description;

    const project = await this.prisma.project.create({
      data,
      include: {
        client: {
          select: {
            id: true,
            name: true,
            nameEn: true,
          },
        },
      },
    });

    return this.serializeBigInt(project);
  }

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

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

    const data: any = {};

    if (dto.name !== undefined) data.name = dto.name;
    if (dto.nameEn !== undefined) data.nameEn = dto.nameEn;
    if (dto.clientId !== undefined) data.clientId = dto.clientId;
    if (dto.status !== undefined) data.status = dto.status;
    if (dto.value !== undefined) data.value = BigInt(dto.value);
    if (dto.startDate !== undefined) data.startDate = new Date(dto.startDate);
    if (dto.endDate !== undefined) data.endDate = new Date(dto.endDate);
    if (dto.description !== undefined) data.description = dto.description;

    const updated = await this.prisma.project.update({
      where: { id },
      data,
      include: {
        client: {
          select: {
            id: true,
            name: true,
            nameEn: true,
          },
        },
      },
    });

    return this.serializeBigInt(updated);
  }

  async updateStatus(userId: number, id: number, status: string) {
    const existing = await this.prisma.project.findFirst({
      where: { id, userId },
    });

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

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

    return this.serializeBigInt(updated);
  }

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

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

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

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

  async bulkRemove(userId: number, ids: number[]) {
    // Verify all projects belong to the user
    const projects = await this.prisma.project.findMany({
      where: {
        id: { in: ids },
        userId,
      },
      select: { id: true },
    });

    const foundIds = projects.map((p) => p.id);
    const missingIds = ids.filter((id) => !foundIds.includes(id));

    if (missingIds.length > 0) {
      throw new NotFoundException(
        `Projects with IDs ${missingIds.join(', ')} not found`,
      );
    }

    await this.prisma.project.deleteMany({
      where: {
        id: { in: ids },
        userId,
      },
    });

    return { message: `${ids.length} projects have been deleted`, deletedIds: ids };
  }

  async getSummary(userId: number) {
    const [totalProjects, inProgress, completed, totalValueResult] =
      await Promise.all([
        this.prisma.project.count({
          where: { userId },
        }),
        this.prisma.project.count({
          where: {
            userId,
            status: {
              in: [
                'estimate-in-progress',
                'contract-completed',
                'tax-invoice-issued',
                'payment-pending',
              ],
            },
          },
        }),
        this.prisma.project.count({
          where: {
            userId,
            status: 'payment-completed',
          },
        }),
        this.prisma.project.aggregate({
          where: { userId },
          _sum: { value: true },
        }),
      ]);

    return {
      totalProjects,
      inProgress,
      completed,
      totalValue: totalValueResult._sum.value
        ? Number(totalValueResult._sum.value)
        : 0,
    };
  }
}
