import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../database/prisma.service';
import { LeadFilterDto } from './dto/lead-filter.dto';
import { CreateLeadDto } from './dto/create-lead.dto';
import { UpdateLeadDto } from './dto/update-lead.dto';
import { LeadCursor } from './dto/lead-cursor.dto';

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

  async findAll(filters: LeadFilterDto) {
    const limit = filters.limit ?? 50;

    let cursor: LeadCursor | null = null;

    if (filters.cursor) {
      try {
        const decoded = Buffer.from(filters.cursor, 'base64').toString('utf8');
        cursor = JSON.parse(decoded) as LeadCursor;
      } catch {
        throw new Error('Invalid cursor');
      }
    }

    const where: any = {};

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

    if (cursor) {
      if (cursor.status === null) {
        where.status = null;
        where.OR = [
          {
            createdAt: {
              lt: new Date(cursor.createdAt),
            },
          },
          {
            createdAt: new Date(cursor.createdAt),
            id: {
              lt: cursor.id,
            },
          },
        ];
      } else {
        where.OR = [
          {
            status: {
              gt: cursor.status,
            },
          },
          {
            status: null,
          },
          {
            status: cursor.status,
            createdAt: {
              lt: new Date(cursor.createdAt),
            },
          },
          {
            status: cursor.status,
            createdAt: new Date(cursor.createdAt),
            id: {
              lt: cursor.id,
            },
          },
        ];
      }
    }

    const leads = await this.prisma.lead.findMany({
      where,
      orderBy: [
        {
          status: 'asc',
        },
        {
          createdAt: 'desc',
        },
        {
          id: 'desc',
        },
      ],
      take: limit + 1,
    });

    const hasNextPage = leads.length > limit;

    if (hasNextPage) {
      leads.pop();
    }

    const lastLead = leads[leads.length - 1];

    const nextCursor =
      hasNextPage && lastLead
        ? Buffer.from(
            JSON.stringify({
              status: lastLead.status,
              createdAt: lastLead.createdAt.toISOString(),
              id: lastLead.id,
            }),
          ).toString('base64')
        : null;

    return {
      data: leads,
      pagination: {
        limit,
        nextCursor,
        hasNextPage,
      },
    };
  }

  async findOne(id: number) {
    const lead = await this.prisma.lead.findUnique({
      where: { id },
      include: {
        customers: true,
      },
    });

    if (!lead) {
      throw new NotFoundException(`Lead with id ${id} not found`);
    }

    return lead;
  }

  async create(dto: CreateLeadDto) {
    return this.prisma.lead.create({
      data: dto,
    });
  }

  async update(id: number, dto: UpdateLeadDto) {
    await this.findOne(id);

    return this.prisma.lead.update({
      where: { id },
      data: dto,
    });
  }

  async remove(id: number) {
    const lead = await this.findOne(id);

    if (lead.customers.length > 0) {
      throw new Error('Cannot delete a lead that has customers');
    }

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

    return {
      message: 'Lead deleted successfully',
      id,
    };
  }
}
