import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import {
  Prisma,
  PaymentDirection,
  PaymentMethod,
  PaymentRelatedType,
  PaymentStatus,
  PreDelContractStatus,
  RobotLifecycleEventType,
  RobotEventRelatedType,
} from '@prisma/client';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';

export interface CreatePaymentInput {
  paymentNo: string;
  relatedType: PaymentRelatedType;
  relatedId: string;
  robotUnitId?: string;
  direction: PaymentDirection;
  amount: number;
  currencyCode: string;
  paymentMethod?: PaymentMethod;
  paymentStatus?: PaymentStatus;
  preDelContractStatus?: PreDelContractStatus;
  paidAt?: Date | string;
  sapClearingDocNo?: string;
  depositTransferFormAttachmentId?: string;
  depositTransferContractAttachmentId?: string;
}

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

  constructor(private readonly prisma: PrismaService) {}

  async create(input: CreatePaymentInput, userId: string, organizationId: string) {
    return this.prisma.$transaction(async (tx) => {
      const record = await tx.paymentRecord.create({
        data: {
          paymentNo: input.paymentNo,
          relatedType: input.relatedType,
          relatedId: input.relatedId,
          robotUnitId: input.robotUnitId,
          direction: input.direction,
          amount: input.amount,
          currencyCode: input.currencyCode,
          paymentMethod: input.paymentMethod,
          paymentStatus: input.paymentStatus ?? PaymentStatus.NOT_PAID_YET,
          preDelContractStatus: input.preDelContractStatus,
          paidAt: input.paidAt ? new Date(input.paidAt) : null,
          sapClearingDocNo: input.sapClearingDocNo,
          depositTransferFormAttachmentId: input.depositTransferFormAttachmentId,
          depositTransferContractAttachmentId: input.depositTransferContractAttachmentId,
          organizationId,
          createdById: userId,
        },
      });
      // 关联到 robot 时写一条 payment_collected event
      if (input.robotUnitId && record.paymentStatus === PaymentStatus.PAID) {
        await tx.robotLifecycleEvent.create({
          data: {
            robotUnitId: input.robotUnitId,
            eventType: RobotLifecycleEventType.payment_collected,
            actorUserId: userId,
            relatedType: RobotEventRelatedType.PAYMENT,
            relatedId: record.id,
            payload: { amount: input.amount, currencyCode: input.currencyCode } as Prisma.InputJsonValue,
            occurredAt: record.paidAt ?? new Date(),
            organizationId,
            createdById: userId,
          },
        });
      }
      return record;
    });
  }

  async findAll(q: {
    relatedType?: PaymentRelatedType;
    relatedId?: string;
    robotUnitId?: string;
    direction?: PaymentDirection;
    status?: PaymentStatus;
    page?: number;
    limit?: number;
  }) {
    const { page = 1, limit = 20 } = q;
    const where: Prisma.PaymentRecordWhereInput = { deletedAt: null };
    if (q.relatedType) where.relatedType = q.relatedType;
    if (q.relatedId) where.relatedId = q.relatedId;
    if (q.robotUnitId) where.robotUnitId = q.robotUnitId;
    if (q.direction) where.direction = q.direction;
    if (q.status) where.paymentStatus = q.status;
    const [items, total] = await Promise.all([
      this.prisma.paymentRecord.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
      }),
      this.prisma.paymentRecord.count({ where }),
    ]);
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  async findOne(id: string) {
    const r = await this.prisma.paymentRecord.findFirst({ where: { id, deletedAt: null } });
    if (!r) throw new NotFoundException(`PaymentRecord ${id} 不存在`);
    return r;
  }

  /**
   * 标记付款 PAID — 同事务写 payment_collected event（如关联 robot）
   */
  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async markPaid(id: string, paidAt: Date | string, userId: string) {
    const existing = await this.prisma.paymentRecord.findFirst({
      where: { id, deletedAt: null },
    });
    if (!existing) throw new NotFoundException(`PaymentRecord ${id} 不存在`);
    return this.prisma.$transaction(async (tx) => {
      await tx.paymentRecord.update({
        where: { id },
        data: {
          paymentStatus: PaymentStatus.PAID,
          paidAt: new Date(paidAt),
        },
      });
      if (existing.robotUnitId) {
        await tx.robotLifecycleEvent.create({
          data: {
            robotUnitId: existing.robotUnitId,
            eventType: RobotLifecycleEventType.payment_collected,
            actorUserId: userId,
            relatedType: RobotEventRelatedType.PAYMENT,
            relatedId: id,
            payload: { amount: existing.amount, paymentNo: existing.paymentNo } as Prisma.InputJsonValue,
            occurredAt: new Date(paidAt),
            organizationId: existing.organizationId,
            createdById: userId,
          },
        });
      }
      // 走 tx 读，避免 this.findOne 走外层 prisma 读到 update 前的状态（read-your-writes inside-tx）
      const updated = await tx.paymentRecord.findFirst({ where: { id, deletedAt: null } });
      if (!updated) throw new NotFoundException(`PaymentRecord ${id} 不存在`);
      return updated;
    });
  }
}
