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

export interface CreateSalesOrderInput {
  soNo: string;
  customerId: string;
  salesPersonId?: string;
  mentorId?: string;
  menteeId?: string;
  currencyCode: string;
  contractStatus?: SalesContractStatus;
  notes?: string;
  lines: Array<{
    lineNo: number;
    lineType: SalesLineType;
    unitPrice: number;
    discount?: number;
    currencyCode: string;
    robotUnitId?: string;
    expectedDeliveryDate?: Date | string;
  }>;
}

export interface UpdateSalesOrderInput {
  status?: SalesOrderStatus;
  contractStatus?: SalesContractStatus;
  signedAt?: Date | string;
  closedAt?: Date | string;
  notes?: string;
  d365SoId?: string;
}

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

  constructor(private readonly prisma: PrismaService) {}

  async create(input: CreateSalesOrderInput, userId: string, organizationId: string) {
    if (!input.lines?.length) throw new BadRequestException('SalesOrder 必须至少一条 line');
    const customer = await this.prisma.customer.findUnique({ where: { id: input.customerId } });
    if (!customer || customer.deletedAt) throw new BadRequestException('Customer 不存在或已删除');
    const totalAmount = input.lines.reduce(
      (s, l) => s + (l.unitPrice - (l.discount ?? 0)),
      0,
    );
    return this.prisma.salesOrder.create({
      data: {
        soNo: input.soNo,
        customerId: input.customerId,
        salesPersonId: input.salesPersonId,
        mentorId: input.mentorId,
        menteeId: input.menteeId,
        currencyCode: input.currencyCode,
        totalAmount,
        contractStatus: input.contractStatus ?? SalesContractStatus.DRAFT,
        status: SalesOrderStatus.RESERVED,
        notes: input.notes,
        organizationId,
        createdById: userId,
        lines: {
          create: input.lines.map((l) => ({
            lineNo: l.lineNo,
            lineType: l.lineType,
            unitPrice: l.unitPrice,
            discount: l.discount,
            netAmount: l.unitPrice - (l.discount ?? 0),
            currencyCode: l.currencyCode,
            robotUnitId: l.robotUnitId,
            expectedDeliveryDate: l.expectedDeliveryDate ? new Date(l.expectedDeliveryDate) : null,
          })),
        },
      },
      include: { lines: true },
    });
  }

  async findAll(query: {
    status?: SalesOrderStatus | SalesOrderStatus[];
    customerId?: string;
    search?: string;
    includeDeleted?: boolean;
    page?: number;
    limit?: number;
  }) {
    const { page = 1, limit = 20, status, customerId, search, includeDeleted } = query;
    const where: Prisma.SalesOrderWhereInput = {
      ...(includeDeleted ? {} : { deletedAt: null }),
    };
    if (status) where.status = Array.isArray(status) ? { in: status } : status;
    if (customerId) where.customerId = customerId;
    if (search) {
      where.OR = [
        { soNo: { contains: search, mode: 'insensitive' } },
        { d365SoId: { contains: search, mode: 'insensitive' } },
      ];
    }
    const [items, total] = await Promise.all([
      this.prisma.salesOrder.findMany({
        where,
        skip: (page - 1) * limit,
        take: limit,
        orderBy: { createdAt: 'desc' },
        include: { lines: true, _count: { select: { deliveryRequests: true } } },
      }),
      this.prisma.salesOrder.count({ where }),
    ]);
    return { items, total, page, limit, totalPages: Math.ceil(total / limit) };
  }

  async findOne(id: string) {
    const so = await this.prisma.salesOrder.findFirst({
      where: { id, deletedAt: null },
      include: { lines: true, deliveryRequests: { take: 50 } },
    });
    if (!so) throw new NotFoundException(`SalesOrder ${id} 不存在`);
    return so;
  }

  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async update(id: string, input: UpdateSalesOrderInput, userId: string) {
    const existing = await this.prisma.salesOrder.findFirst({ where: { id, deletedAt: null } });
    if (!existing) throw new NotFoundException(`SalesOrder ${id} 不存在`);
    await this.prisma.salesOrder.update({
      where: { id },
      data: {
        status: input.status,
        contractStatus: input.contractStatus,
        signedAt: input.signedAt ? new Date(input.signedAt) : undefined,
        closedAt: input.closedAt ? new Date(input.closedAt) : undefined,
        notes: input.notes,
        d365SoId: input.d365SoId,
      },
    });
    this.logger.log(`SO ${existing.soNo} updated by ${userId}`);
    return this.findOne(id);
  }

  @SkipAssertAccess('上方 findFirst({ id }) 已抓到对象')
  async softDelete(id: string, _userId: string) {
    const existing = await this.prisma.salesOrder.findFirst({ where: { id, deletedAt: null } });
    if (!existing) throw new NotFoundException(`SalesOrder ${id} 不存在`);
    await this.prisma.salesOrder.update({ where: { id }, data: { deletedAt: new Date() } });
    return { message: 'SalesOrder soft-deleted', id };
  }
}
