import {
  Injectable,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { Prisma, InventoryOperationType } from '@prisma/client';
import {
  CheckInDto,
  CheckOutDto,
  AdjustInventoryDto,
  TransferInventoryDto,
  QueryInventoryLogsDto,
  InventoryStatsDto,
  BulkInventoryOperationDto,
} from '../dto/inventory.dto';
import { PartsService } from './parts.service';

@Injectable()
export class InventoryService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly partsService: PartsService,
  ) {}

  /**
   * 扫码入库 (Check-in)
   */
  async checkIn(checkInDto: CheckInDto, userId: string, userName: string) {
    const { code, quantity, operationType, station, warehouseLocation, reason, referenceType, referenceId, deviceInfo, metadata } = checkInDto;

    // 通过 code 查找零件
    const part = await this.partsService.findByCode(code);

    if (quantity <= 0) {
      throw new BadRequestException('Quantity must be greater than 0');
    }

    const previousStock = part.currentStock;
    const newStock = previousStock + quantity;

    // 使用事务更新库存并记录日志
    const [updatedPart, log] = await this.prisma.$transaction([
      this.prisma.part.update({
        where: { id: part.id },
        data: { currentStock: newStock },
      }),
      this.prisma.inventoryLog.create({
        data: {
          partId: part.id,
          operationType: operationType || InventoryOperationType.CHECK_IN,
          quantity,
          previousStock,
          newStock,
          station: station || part.station,
          warehouseLocation: warehouseLocation || part.warehouseLocation,
          reason,
          referenceType,
          referenceId,
          operatorId: userId,
          operatorName: userName,
          scannedCode: code,
          deviceInfo,
          metadata: metadata || {},
        },
      }),
    ]);

    // 检查库存告警
    await this.partsService.checkStockAndCreateAlerts(part.id);

    return {
      part: updatedPart,
      log,
      message: 'Check-in successful',
    };
  }

  /**
   * 扫码出库 (Check-out)
   */
  async checkOut(checkOutDto: CheckOutDto, userId: string, userName: string) {
    const { code, quantity, station, warehouseLocation, reason, referenceType, referenceId, deviceInfo, metadata } = checkOutDto;

    // 通过 code 查找零件
    const part = await this.partsService.findByCode(code);

    if (quantity <= 0) {
      throw new BadRequestException('Quantity must be greater than 0');
    }

    if (part.currentStock < quantity) {
      throw new BadRequestException(
        `Insufficient stock. Available: ${part.currentStock}, Requested: ${quantity}`,
      );
    }

    const previousStock = part.currentStock;
    const newStock = previousStock - quantity;

    // 使用事务更新库存并记录日志
    const [updatedPart, log] = await this.prisma.$transaction([
      this.prisma.part.update({
        where: { id: part.id },
        data: { currentStock: newStock },
      }),
      this.prisma.inventoryLog.create({
        data: {
          partId: part.id,
          operationType: InventoryOperationType.CHECK_OUT,
          quantity: -quantity, // 负数表示出库
          previousStock,
          newStock,
          station,
          warehouseLocation: warehouseLocation || part.warehouseLocation,
          reason,
          referenceType,
          referenceId,
          operatorId: userId,
          operatorName: userName,
          scannedCode: code,
          deviceInfo,
          metadata: metadata || {},
        },
      }),
    ]);

    // 检查库存告警
    await this.partsService.checkStockAndCreateAlerts(part.id);

    return {
      part: updatedPart,
      log,
      message: 'Check-out successful',
    };
  }

  /**
   * 库存调整（需要 Admin 权限）
   */
  async adjustInventory(
    adjustDto: AdjustInventoryDto,
    userId: string,
    userName: string,
  ) {
    const { partId, newStock, reason, station, warehouseLocation, metadata } = adjustDto;

    if (newStock < 0) {
      throw new BadRequestException('Stock cannot be negative');
    }

    const part = await this.partsService.findOne(partId);
    const previousStock = part.currentStock;
    const quantity = newStock - previousStock;

    // 使用事务更新库存并记录日志
    const [updatedPart, log] = await this.prisma.$transaction([
      this.prisma.part.update({
        where: { id: partId },
        data: { currentStock: newStock },
      }),
      this.prisma.inventoryLog.create({
        data: {
          partId,
          operationType: InventoryOperationType.ADJUSTMENT,
          quantity,
          previousStock,
          newStock,
          station: station || part.station,
          warehouseLocation: warehouseLocation || part.warehouseLocation,
          reason,
          operatorId: userId,
          operatorName: userName,
          metadata: metadata || {},
        },
      }),
    ]);

    // 检查库存告警
    await this.partsService.checkStockAndCreateAlerts(partId);

    return {
      part: updatedPart,
      log,
      message: 'Inventory adjusted successfully',
    };
  }

  /**
   * 库存转移
   */
  async transferInventory(
    transferDto: TransferInventoryDto,
    userId: string,
    userName: string,
  ) {
    const {
      partId,
      quantity,
      fromStation,
      toStation,
      fromWarehouseLocation,
      toWarehouseLocation,
      reason,
      metadata,
    } = transferDto;

    if (quantity <= 0) {
      throw new BadRequestException('Quantity must be greater than 0');
    }

    const part = await this.partsService.findOne(partId);

    if (part.currentStock < quantity) {
      throw new BadRequestException(
        `Insufficient stock for transfer. Available: ${part.currentStock}, Requested: ${quantity}`,
      );
    }

    // 记录转出日志
    await this.prisma.inventoryLog.create({
      data: {
        partId,
        operationType: InventoryOperationType.TRANSFER,
        quantity: -quantity,
        previousStock: part.currentStock,
        newStock: part.currentStock,
        station: fromStation,
        warehouseLocation: fromWarehouseLocation,
        reason: `Transfer to ${toStation}${toWarehouseLocation ? ` - ${toWarehouseLocation}` : ''}: ${reason || ''}`,
        operatorId: userId,
        operatorName: userName,
        metadata: {
          ...metadata,
          transferType: 'OUT',
          toStation,
          toWarehouseLocation,
        },
      },
    });

    // 记录转入日志
    const log = await this.prisma.inventoryLog.create({
      data: {
        partId,
        operationType: InventoryOperationType.TRANSFER,
        quantity,
        previousStock: part.currentStock,
        newStock: part.currentStock,
        station: toStation,
        warehouseLocation: toWarehouseLocation,
        reason: `Transfer from ${fromStation}${fromWarehouseLocation ? ` - ${fromWarehouseLocation}` : ''}: ${reason || ''}`,
        operatorId: userId,
        operatorName: userName,
        metadata: {
          ...metadata,
          transferType: 'IN',
          fromStation,
          fromWarehouseLocation,
        },
      },
    });

    return {
      log,
      message: 'Inventory transferred successfully',
    };
  }

  /**
   * 查询库存日志
   */
  async findLogs(query: QueryInventoryLogsDto) {
    const {
      partId,
      partNumber,
      operationType,
      station,
      warehouseLocation,
      operatorId,
      startDate,
      endDate,
      page = 1,
      limit = 50,
      sortBy = 'createdAt',
      sortOrder = 'desc',
    } = query;

    const where: Prisma.InventoryLogWhereInput = {};

    if (partId) {
      where.partId = partId;
    }

    if (partNumber) {
      where.part = {
        partNumber: { contains: partNumber, mode: 'insensitive' },
      };
    }

    if (operationType) {
      where.operationType = operationType;
    }

    if (station) {
      where.station = station;
    }

    if (warehouseLocation) {
      where.warehouseLocation = warehouseLocation;
    }

    if (operatorId) {
      where.operatorId = operatorId;
    }

    if (startDate || endDate) {
      where.createdAt = {};
      if (startDate) {
        where.createdAt.gte = new Date(startDate);
      }
      if (endDate) {
        where.createdAt.lte = new Date(endDate);
      }
    }

    const skip = (page - 1) * limit;

    const [items, total] = await Promise.all([
      this.prisma.inventoryLog.findMany({
        where,
        skip,
        take: limit,
        orderBy: { [sortBy]: sortOrder },
        include: {
          part: {
            select: {
              id: true,
              partNumber: true,
              partNameEn: true,
              partNameCn: true,
              unit: true,
              currentStock: true,
            },
          },
        },
      }),
      this.prisma.inventoryLog.count({ where }),
    ]);

    return {
      items,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * 获取零件的库存历史
   */
  async getPartInventoryHistory(partId: string, days: number = 30) {
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - days);

    const logs = await this.prisma.inventoryLog.findMany({
      where: {
        partId,
        createdAt: { gte: startDate },
      },
      orderBy: { createdAt: 'asc' },
      select: {
        createdAt: true,
        operationType: true,
        quantity: true,
        previousStock: true,
        newStock: true,
        station: true,
        operatorName: true,
      },
    });

    return logs;
  }

  /**
   * 获取库存统计
   */
  async getStats(query: InventoryStatsDto) {
    const {
      station,
      warehouseLocation,
      category,
      startDate,
      endDate,
      operationType,
    } = query;

    const where: Prisma.InventoryLogWhereInput = {};

    if (station) {
      where.station = station;
    }

    if (warehouseLocation) {
      where.warehouseLocation = warehouseLocation;
    }

    if (operationType) {
      where.operationType = operationType;
    }

    if (startDate || endDate) {
      where.createdAt = {};
      if (startDate) {
        where.createdAt.gte = new Date(startDate);
      }
      if (endDate) {
        where.createdAt.lte = new Date(endDate);
      }
    }

    // category 字段已移除，如需按类别过滤，请使用 PartGroup
    // if (category) {
    //   where.part = {
    //     category,
    //   };
    // }

    const [
      totalOperations,
      checkIns,
      checkOuts,
      adjustments,
      operationsByType,
      operationsByStation,
      topParts,
    ] = await Promise.all([
      // 总操作数
      this.prisma.inventoryLog.count({ where }),

      // 入库次数
      this.prisma.inventoryLog.count({
        where: {
          ...where,
          operationType: InventoryOperationType.CHECK_IN,
        },
      }),

      // 出库次数
      this.prisma.inventoryLog.count({
        where: {
          ...where,
          operationType: InventoryOperationType.CHECK_OUT,
        },
      }),

      // 调整次数
      this.prisma.inventoryLog.count({
        where: {
          ...where,
          operationType: InventoryOperationType.ADJUSTMENT,
        },
      }),

      // 按操作类型统计
      this.prisma.inventoryLog.groupBy({
        by: ['operationType'],
        where,
        _count: true,
        _sum: {
          quantity: true,
        },
      }),

      // 按工位统计
      this.prisma.inventoryLog.groupBy({
        by: ['station'],
        where: {
          ...where,
          station: { not: null },
        },
        _count: true,
      }),

      // 最活跃的零件
      this.prisma.inventoryLog.groupBy({
        by: ['partId'],
        where,
        _count: true,
        orderBy: {
          _count: {
            partId: 'desc',
          },
        },
        take: 10,
      }),
    ]);

    // 获取最活跃零件的详细信息
    const topPartIds = topParts.map((p) => p.partId);
    const topPartsDetails = await this.prisma.part.findMany({
      where: { id: { in: topPartIds } },
      select: {
        id: true,
        partNumber: true,
        partNameEn: true,
        partNameCn: true,
        currentStock: true,
      },
    });

    const topPartsWithDetails = topParts.map((p) => {
      const detail = topPartsDetails.find((d) => d.id === p.partId);
      return {
        ...p,
        part: detail,
      };
    });

    return {
      totalOperations,
      checkIns,
      checkOuts,
      adjustments,
      operationsByType,
      operationsByStation,
      topParts: topPartsWithDetails,
    };
  }

  /**
   * 批量库存操作
   */
  async bulkOperation(
    bulkDto: BulkInventoryOperationDto,
    userId: string,
    userName: string,
  ) {
    const { operations, skipErrors = true } = bulkDto;

    const results = {
      total: operations.length,
      success: 0,
      failed: 0,
      errors: [] as any[],
    };

    for (const op of operations) {
      try {
        const part = await this.partsService.findOne(op.partId);

        switch (op.operationType) {
          case InventoryOperationType.CHECK_IN:
          case InventoryOperationType.RECEIVING:
          case InventoryOperationType.RETURN:
            await this.checkIn(
              {
                code: part.partNumber,
                quantity: op.quantity,
                operationType: op.operationType,
                station: op.station,
                warehouseLocation: op.warehouseLocation,
                reason: op.reason,
              },
              userId,
              userName,
            );
            break;

          case InventoryOperationType.CHECK_OUT:
          case InventoryOperationType.SCRAP:
            await this.checkOut(
              {
                code: part.partNumber,
                quantity: op.quantity,
                station: op.station || part.station || '',
                warehouseLocation: op.warehouseLocation,
                reason: op.reason,
              },
              userId,
              userName,
            );
            break;

          case InventoryOperationType.ADJUSTMENT:
            await this.adjustInventory(
              {
                partId: op.partId,
                newStock: part.currentStock + op.quantity,
                reason: op.reason || 'Bulk adjustment',
                station: op.station,
                warehouseLocation: op.warehouseLocation,
              },
              userId,
              userName,
            );
            break;

          default:
            throw new BadRequestException(
              `Unsupported operation type: ${op.operationType}`,
            );
        }

        results.success++;
      } catch (error) {
        results.failed++;
        results.errors.push({
          partId: op.partId,
          error: error.message,
        });

        if (!skipErrors) {
          throw error;
        }
      }
    }

    return results;
  }
}

