import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { Prisma, RentalAgreementStatus } from '@prisma/client';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';

export interface CreateRentalAgreementInput {
  robotUnitId: string;
  customerId: string;
  startAt: Date | string;
  endAt: Date | string;
  monthlyRate: number;
  currencyCode: string;
  contractAttachmentId?: string;
}

@Injectable()
export class RentalService {
  private readonly logger = new Logger(RentalService.name);

  constructor(private readonly prisma: PrismaService) {}

  private monthsBetween(start: Date, end: Date) {
    return Math.max(
      1,
      (end.getFullYear() - start.getFullYear()) * 12 +
        (end.getMonth() - start.getMonth()),
    );
  }

  async createAgreement(input: CreateRentalAgreementInput, userId: string, organizationId: string) {
    const start = new Date(input.startAt);
    const end = new Date(input.endAt);
    if (end <= start) throw new BadRequestException('endAt 必须晚于 startAt');
    const periodMonths = this.monthsBetween(start, end);
    return this.prisma.$transaction(async (tx) => {
      const agreement = await tx.rentalAgreement.create({
        data: {
          robotUnitId: input.robotUnitId,
          customerId: input.customerId,
          startAt: start,
          endAt: end,
          periodMonths,
          monthlyRate: input.monthlyRate,
          currencyCode: input.currencyCode,
          status: RentalAgreementStatus.ACTIVE,
          contractAttachmentId: input.contractAttachmentId,
          organizationId,
          createdById: userId,
        },
      });
      // 自动生成 periodMonths 期 schedule
      const schedules = [];
      for (let m = 0; m < periodMonths; m++) {
        const dueDate = new Date(start);
        dueDate.setMonth(dueDate.getMonth() + m);
        schedules.push({
          rentalAgreementId: agreement.id,
          dueDate,
          amount: input.monthlyRate,
          currencyCode: input.currencyCode,
        });
      }
      if (schedules.length > 0) {
        await tx.rentalPaymentSchedule.createMany({ data: schedules });
      }
      return agreement;
    });
  }

  async findAll(q: {
    status?: RentalAgreementStatus;
    customerId?: string;
    robotUnitId?: string;
    page?: number;
    limit?: number;
  }) {
    const { page = 1, limit = 20 } = q;
    const where: Prisma.RentalAgreementWhereInput = { deletedAt: null };
    if (q.status) where.status = q.status;
    if (q.customerId) where.customerId = q.customerId;
    if (q.robotUnitId) where.robotUnitId = q.robotUnitId;
    const [items, total] = await Promise.all([
      this.prisma.rentalAgreement.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { startAt: 'desc' },
        include: { _count: { select: { schedules: true } } },
      }),
      this.prisma.rentalAgreement.count({ where }),
    ]);
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  async findOne(id: string) {
    const a = await this.prisma.rentalAgreement.findFirst({
      where: { id, deletedAt: null },
      include: { schedules: { orderBy: { dueDate: 'asc' } } },
    });
    if (!a) throw new NotFoundException(`RentalAgreement ${id} 不存在`);
    return a;
  }

  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async terminate(id: string, _userId: string) {
    const existing = await this.prisma.rentalAgreement.findFirst({
      where: { id, deletedAt: null },
    });
    if (!existing) throw new NotFoundException(`RentalAgreement ${id} 不存在`);
    await this.prisma.rentalAgreement.update({
      where: { id },
      data: { status: RentalAgreementStatus.TERMINATED },
    });
    return this.findOne(id);
  }

  @SkipAssertAccess('上方 findUnique({ id }) 已抓到对象，update 用同一 id；DataScope 经父表 rentalAgreement 的 organizationId 隔离')
  async markScheduleEntryPaid(scheduleId: string, paidPaymentRecordId: string, paidAt: Date | string) {
    const e = await this.prisma.rentalPaymentSchedule.findUnique({ where: { id: scheduleId } });
    if (!e) throw new NotFoundException(`RentalPaymentSchedule ${scheduleId} 不存在`);
    return this.prisma.rentalPaymentSchedule.update({
      where: { id: scheduleId },
      data: { paidPaymentRecordId, paidAt: new Date(paidAt) },
    });
  }
}
