import { BadRequestException, Injectable, Logger, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import {
  Prisma,
  DeliveryRequestStatus,
  DeliveryRequestType,
  DeliveryFormStatus,
  RevenueRecognitionStatus,
  InvoiceStatus,
  RobotLifecycleEventType,
  RobotEventRelatedType,
} from '@prisma/client';
import { SkipAssertAccess } from '@common/decorators/skip-assert-access.decorator';
import { SnapshotProjectorService } from '../services/snapshot-projector.service';

export interface CreateDeliveryRequestInput {
  deliveryNo: string;
  salesOrderId: string;
  customerId: string;
  requestType: DeliveryRequestType;
  expectedDate: Date | string;
  notes?: string;
}

export interface CreateDeliveryFulfillmentInput {
  deliveryRequestId: string;
  robotUnitId: string;
  deliveredAt: Date | string;
  signedAt?: Date | string;
  signedFormStatus?: DeliveryFormStatus;
  acceptanceFormStatus?: DeliveryFormStatus;
  acceptanceFormPreDel?: DeliveryFormStatus;
  acceptanceFormAttachmentId?: string;
  sapPgiDocNo?: string;
  cost?: number;
  notePreDel?: string;
  additionalShippingNotes?: string;
}

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

  constructor(
    private readonly prisma: PrismaService,
    private readonly projector: SnapshotProjectorService,
  ) {}

  // ---- DeliveryRequest ----

  async createRequest(input: CreateDeliveryRequestInput, userId: string, organizationId: string) {
    const so = await this.prisma.salesOrder.findUnique({ where: { id: input.salesOrderId } });
    if (!so || so.deletedAt) throw new BadRequestException('SalesOrder 不存在');
    return this.prisma.deliveryRequest.create({
      data: {
        deliveryNo: input.deliveryNo,
        salesOrderId: input.salesOrderId,
        customerId: input.customerId,
        requestType: input.requestType,
        expectedDate: new Date(input.expectedDate),
        status: DeliveryRequestStatus.REQUESTED,
        notes: input.notes,
        organizationId,
        createdById: userId,
      },
    });
  }

  async listRequests(q: {
    salesOrderId?: string;
    customerId?: string;
    status?: DeliveryRequestStatus;
    page?: number;
    limit?: number;
  }) {
    const { page = 1, limit = 20 } = q;
    const where: Prisma.DeliveryRequestWhereInput = { deletedAt: null };
    if (q.salesOrderId) where.salesOrderId = q.salesOrderId;
    if (q.customerId) where.customerId = q.customerId;
    if (q.status) where.status = q.status;
    const [items, total] = await Promise.all([
      this.prisma.deliveryRequest.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { expectedDate: 'desc' },
        include: { _count: { select: { fulfillments: true } } },
      }),
      this.prisma.deliveryRequest.count({ where }),
    ]);
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  async findRequest(id: string) {
    const r = await this.prisma.deliveryRequest.findFirst({
      where: { id, deletedAt: null },
      include: { fulfillments: true, salesOrder: { select: { id: true, soNo: true } } },
    });
    if (!r) throw new NotFoundException(`DeliveryRequest ${id} 不存在`);
    return r;
  }

  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async updateRequest(
    id: string,
    input: { status?: DeliveryRequestStatus; expectedDate?: Date | string; notes?: string },
    _userId: string,
  ) {
    const existing = await this.prisma.deliveryRequest.findFirst({
      where: { id, deletedAt: null },
    });
    if (!existing) throw new NotFoundException(`DeliveryRequest ${id} 不存在`);
    await this.prisma.deliveryRequest.update({
      where: { id },
      data: {
        status: input.status,
        expectedDate: input.expectedDate ? new Date(input.expectedDate) : undefined,
        notes: input.notes,
      },
    });
    return this.findRequest(id);
  }

  // ---- DeliveryFulfillment ----

  /**
   * 创建 fulfillment + 同事务写 delivery_signed event + 刷新 Snapshot.warrantyStatus/currentCustomerId
   */
  async createFulfillment(input: CreateDeliveryFulfillmentInput, userId: string, organizationId: string) {
    const req = await this.prisma.deliveryRequest.findUnique({
      where: { id: input.deliveryRequestId },
      include: { salesOrder: { select: { id: true } } },
    });
    if (!req) throw new BadRequestException('DeliveryRequest 不存在');
    const unit = await this.prisma.robotUnit.findUnique({
      where: { id: input.robotUnitId },
      include: { snapshot: true },
    });
    if (!unit || !unit.snapshot) throw new BadRequestException('RobotUnit 或 snapshot 不存在');

    return this.prisma.$transaction(async (tx) => {
      const f = await tx.deliveryFulfillment.create({
        data: {
          deliveryRequestId: input.deliveryRequestId,
          robotUnitId: input.robotUnitId,
          deliveredAt: new Date(input.deliveredAt),
          signedAt: input.signedAt ? new Date(input.signedAt) : null,
          signedFormStatus: input.signedFormStatus,
          acceptanceFormStatus: input.acceptanceFormStatus,
          acceptanceFormPreDel: input.acceptanceFormPreDel,
          acceptanceFormAttachmentId: input.acceptanceFormAttachmentId,
          sapPgiDocNo: input.sapPgiDocNo,
          cost: input.cost,
          notePreDel: input.notePreDel,
          additionalShippingNotes: input.additionalShippingNotes,
          organizationId,
          createdById: userId,
        },
      });

      const event = await tx.robotLifecycleEvent.create({
        data: {
          robotUnitId: input.robotUnitId,
          eventType: RobotLifecycleEventType.delivery_signed,
          actorUserId: userId,
          customerId: req.customerId,
          relatedType: RobotEventRelatedType.DELIVERY_FULFILLMENT,
          relatedId: f.id,
          payload: { salesOrderId: req.salesOrder?.id, deliveryRequestId: req.id } as Prisma.InputJsonValue,
          occurredAt: new Date(input.deliveredAt),
          organizationId,
          createdById: userId,
        },
      });
      await this.projector.project(tx, event, unit.snapshot!.version);
      return f;
    });
  }

  async findFulfillment(id: string) {
    const f = await this.prisma.deliveryFulfillment.findFirst({
      where: { id, deletedAt: null },
      include: { deliveryRequest: true, robotUnit: { select: { id: true, ffsn: true } } },
    });
    if (!f) throw new NotFoundException(`DeliveryFulfillment ${id} 不存在`);
    return f;
  }

  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async updateFulfillment(
    id: string,
    input: {
      signedFormStatus?: DeliveryFormStatus;
      acceptanceFormStatus?: DeliveryFormStatus;
      acceptanceFormPreDel?: DeliveryFormStatus;
      revenueRecognition?: RevenueRecognitionStatus;
      invoiceStatus?: InvoiceStatus;
      cost?: number;
      grossMargin?: number;
      notePreDel?: string;
      additionalShippingNotes?: string;
    },
  ) {
    const existing = await this.prisma.deliveryFulfillment.findFirst({
      where: { id, deletedAt: null },
    });
    if (!existing) throw new NotFoundException(`DeliveryFulfillment ${id} 不存在`);
    await this.prisma.deliveryFulfillment.update({
      where: { id },
      data: input,
    });
    return this.findFulfillment(id);
  }
}
