import { ConsumeInventoryDto } from './dto/consume-inventory.dto';
import { Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { CreateInventoryDto } from './dto/create-inventory.dto';
import { UpdateInventoryDto } from './dto/update-inventory.dto';

type InventoryQuery = {
  location?: string;
  sort?: string;
};

@Injectable()
export class InventoryService {
  constructor(private prisma: PrismaService) {}

  async findAll(query?: InventoryQuery) {
    const where: Prisma.InventoryItemWhereInput = {};
    const orderBy: Prisma.InventoryItemOrderByWithRelationInput[] = [];

    if (query?.location) {
      where.location = query.location;
    }

    if (query?.sort === 'bestBeforeAsc') {
      orderBy.push({ bestBeforeDate: 'asc' });
    } else if (query?.sort === 'bestBeforeDesc') {
      orderBy.push({ bestBeforeDate: 'desc' });
    } else if (query?.sort === 'nameAsc') {
      orderBy.push({ product: { name: 'asc' } } as any);
    } else if (query?.sort === 'purchaseDateAsc') {
      orderBy.push({ purchaseDate: 'asc' });
    } else if (query?.sort === 'purchaseDateDesc') {
      orderBy.push({ purchaseDate: 'desc' });
    } else {
      orderBy.push({ createdAt: 'desc' });
    }

    return this.prisma.inventoryItem.findMany({
      where,
      include: {
        product: true,
      },
      orderBy,
    });
  }
  
  async consume(id: number, data: ConsumeInventoryDto) {
  const existing = await this.prisma.inventoryItem.findUnique({
    where: { id },
    include: {
      product: true,
    },
  });

  if (!existing) {
    throw new NotFoundException(`Inventory item with id ${id} not found`);
  }

  const currentQuantity = Number(existing.quantity);
  const newQuantity = Math.max(0, currentQuantity - data.amountUsed);

  return this.prisma.$transaction(async (tx) => {
    const updatedItem = await tx.inventoryItem.update({
      where: { id },
      data: {
        quantity: new Prisma.Decimal(newQuantity),
      },
      include: {
        product: true,
      },
    });

    await tx.inventoryConsumption.create({
      data: {
        inventoryItemId: id,
        amountUsed: new Prisma.Decimal(data.amountUsed),
        comment: data.comment?.trim() || null,
      },
    });

    return updatedItem;
  });
}

  async findConsumptionHistory(id: number) {
    const existing = await this.prisma.inventoryItem.findUnique({
    where: { id },
  });

  if (!existing) {
    throw new NotFoundException(`Inventory item with id ${id} not found`);
  }

  return this.prisma.inventoryConsumption.findMany({
    where: {
      inventoryItemId: id,
    },
    select: {
      id: true,
      inventoryItemId: true,
      amountUsed: true,
      comment: true,
      createdAt: true,
      inventoryItem: {
        select: { unit: true },
      },
    },
    orderBy: {
      createdAt: 'desc',
    },
  });
}
  async findExpiring() {
    const now = new Date();

    return this.prisma.inventoryItem.findMany({
      where: {
        bestBeforeDate: {
          not: null,
          gte: now,
        },
      },
      include: {
        product: true,
      },
      orderBy: [{ bestBeforeDate: 'asc' }, { createdAt: 'desc' }],
    });
  }

  async create(data: CreateInventoryDto) {
    const product = await this.prisma.product.findUnique({
      where: { id: data.productId },
  });

  if (!product) {
    throw new NotFoundException('Product not found');
  }

  return this.prisma.inventoryItem.create({
    data: {
      ...data,
      quantity: new Prisma.Decimal(data.quantity),
      location: data.location?.trim() || undefined,
      brand: data.brand?.trim() || undefined,
      origin: data.origin?.trim() || undefined,
      receiptName: data.receiptName?.trim() || undefined,
      suitableFor: data.suitableFor?.trim() || undefined,
      comment: data.comment?.trim() || undefined,
      purchaseDate: data.purchaseDate
        ? new Date(data.purchaseDate)
        : undefined,
      bestBeforeDate: data.bestBeforeDate
        ? new Date(data.bestBeforeDate)
        : undefined,
    },
    include: {
      product: true,
    },
  });
}

  async update(id: number, data: UpdateInventoryDto) {
    const existing = await this.prisma.inventoryItem.findUnique({
      where: { id },
    });

    if (!existing) {
      throw new NotFoundException(`Inventory item with id ${id} not found`);
    }

    if (typeof data.productId === 'number') {
      const product = await this.prisma.product.findUnique({
        where: { id: data.productId },
      });

      if (!product) {
        throw new NotFoundException('Product not found');
      }
    }

    const updateData: Prisma.InventoryItemUpdateInput = {};

    if (typeof data.productId === 'number') {
      updateData.product = {
        connect: { id: data.productId },
      };
    }

    if (typeof data.quantity === 'number') {
      updateData.quantity = new Prisma.Decimal(data.quantity);
    }

    if (typeof data.unit === 'string') {
      updateData.unit = data.unit.trim();
    }

    if (typeof data.location === 'string') {
      updateData.location = data.location.trim();
    }
    
    if (typeof data.brand === 'string') {
      updateData.brand = data.brand.trim();
    }

    if (typeof data.receiptName === 'string') {
      updateData.receiptName = data.receiptName.trim();
    }
    
    if (typeof data.purchaseDate === 'string') {
      updateData.purchaseDate = data.purchaseDate
        ? new Date(data.purchaseDate)
        : null;
    }

    if (typeof data.bestBeforeDate === 'string') {
      updateData.bestBeforeDate = data.bestBeforeDate
        ? new Date(data.bestBeforeDate)
        : null;
    }

    if (typeof data.opened === 'boolean') {
      updateData.opened = data.opened;
    }

    if (typeof data.suitableFor === 'string') {
      updateData.suitableFor = data.suitableFor.trim();
    }

    if (typeof data.comment === 'string') {
      updateData.comment = data.comment.trim();
    }

    return this.prisma.inventoryItem.update({
      where: { id },
      data: updateData,
      include: {
        product: true,
      },
    });
  }

  async remove(id: number) {
    const existing = await this.prisma.inventoryItem.findUnique({ where: { id } });
    if (!existing) {
      throw new NotFoundException(`Inventory item with id ${id} not found`);
    }
    return this.prisma.inventoryItem.delete({ where: { id } });
  }
}