import prisma from '../../config/database';
import { AppError } from '../../shared/middleware/errorHandler';
import { AuditLogger } from '../../shared/utils/auditLogger';
import { Prisma } from '@prisma/client';

interface CreateProductData {
  sku: string;
  name: string;
  nameAr?: string;
  description?: string;
  categoryId: string;
  brand?: string;
  model?: string;
  specifications?: any;
  trackBy?: string;
  costPrice: number;
  sellingPrice: number;
  minStock?: number;
  maxStock?: number;
}

interface UpdateProductData extends Partial<CreateProductData> {}

class ProductsService {
  async create(data: CreateProductData, userId: string) {
    // Check if SKU already exists
    const existing = await prisma.product.findUnique({
      where: { sku: data.sku },
    });

    if (existing) {
      throw new AppError(400, 'SKU already exists');
    }

    const product = await prisma.product.create({
      data: {
        sku: data.sku,
        name: data.name,
        nameAr: data.nameAr,
        description: data.description,
        categoryId: data.categoryId,
        brand: data.brand,
        model: data.model,
        specifications: data.specifications,
        trackBy: data.trackBy || 'QUANTITY',
        costPrice: data.costPrice,
        sellingPrice: data.sellingPrice,
        minStock: data.minStock || 0,
        maxStock: data.maxStock,
        unit: 'PCS', // Default unit
      },
      include: {
        category: true,
      },
    });

    await AuditLogger.log({
      entityType: 'PRODUCT',
      entityId: product.id,
      action: 'CREATE',
      userId,
    });

    return product;
  }

  async findAll(filters: any, page: number, pageSize: number) {
    const skip = (page - 1) * pageSize;

    const where: Prisma.ProductWhereInput = {};

    if (filters.search) {
      where.OR = [
        { name: { contains: filters.search, mode: 'insensitive' } },
        { nameAr: { contains: filters.search, mode: 'insensitive' } },
        { sku: { contains: filters.search, mode: 'insensitive' } },
        { description: { contains: filters.search, mode: 'insensitive' } },
      ];
    }

    if (filters.categoryId) {
      where.categoryId = filters.categoryId;
    }

    if (filters.brand) {
      where.brand = { contains: filters.brand, mode: 'insensitive' };
    }

    const total = await prisma.product.count({ where });

    const products = await prisma.product.findMany({
      where,
      skip,
      take: pageSize,
      include: {
        category: true,
        inventoryItems: {
          include: {
            warehouse: true,
          },
        },
      },
      orderBy: {
        createdAt: 'desc',
      },
    });

    // Calculate total stock for each product
    const productsWithStock = products.map((product) => {
      const totalStock = product.inventoryItems.reduce(
        (sum, item) => sum + item.quantity,
        0
      );
      return {
        ...product,
        totalStock,
      };
    });

    return {
      products: productsWithStock,
      total,
      page,
      pageSize,
    };
  }

  async findById(id: string) {
    const product = await prisma.product.findUnique({
      where: { id },
      include: {
        category: true,
        inventoryItems: {
          include: {
            warehouse: true,
          },
        },
        movements: {
          take: 10,
          orderBy: {
            createdAt: 'desc',
          },
        },
      },
    });

    if (!product) {
      throw new AppError(404, 'Product not found');
    }

    return product;
  }

  async update(id: string, data: UpdateProductData, userId: string) {
    const existing = await prisma.product.findUnique({ where: { id } });

    if (!existing) {
      throw new AppError(404, 'Product not found');
    }

    // Check SKU uniqueness if it's being updated
    if (data.sku && data.sku !== existing.sku) {
      const skuExists = await prisma.product.findUnique({
        where: { sku: data.sku },
      });
      if (skuExists) {
        throw new AppError(400, 'SKU already exists');
      }
    }

    const product = await prisma.product.update({
      where: { id },
      data: {
        sku: data.sku,
        name: data.name,
        nameAr: data.nameAr,
        description: data.description,
        categoryId: data.categoryId,
        brand: data.brand,
        model: data.model,
        specifications: data.specifications,
        trackBy: data.trackBy,
        costPrice: data.costPrice,
        sellingPrice: data.sellingPrice,
        minStock: data.minStock,
        maxStock: data.maxStock,
      },
      include: {
        category: true,
      },
    });

    await AuditLogger.log({
      entityType: 'PRODUCT',
      entityId: product.id,
      action: 'UPDATE',
      userId,
      changes: {
        before: existing,
        after: product,
      },
    });

    return product;
  }

  async delete(id: string, userId: string) {
    const product = await prisma.product.findUnique({ where: { id } });

    if (!product) {
      throw new AppError(404, 'Product not found');
    }

    // Check if product has inventory
    const hasInventory = await prisma.inventoryItem.findFirst({
      where: { productId: id, quantity: { gt: 0 } },
    });

    if (hasInventory) {
      throw new AppError(
        400,
        'Cannot delete product that has inventory stock'
      );
    }

    await prisma.product.delete({ where: { id } });

    await AuditLogger.log({
      entityType: 'PRODUCT',
      entityId: product.id,
      action: 'DELETE',
      userId,
    });

    return { message: 'Product deleted successfully' };
  }

  async adjustStock(
    productId: string,
    warehouseId: string,
    quantity: number,
    type: 'ADD' | 'REMOVE',
    userId: string
  ) {
    const product = await prisma.product.findUnique({ where: { id: productId } });

    if (!product) {
      throw new AppError(404, 'Product not found');
    }

    // Find or create inventory item
    let inventoryItem = await prisma.inventoryItem.findFirst({
      where: {
        productId,
        warehouseId,
      },
    });

    const adjustedQuantity = type === 'ADD' ? quantity : -quantity;

    if (!inventoryItem) {
      if (type === 'REMOVE') {
        throw new AppError(400, 'Cannot remove from non-existent inventory');
      }
      const costPrice = Number(product.costPrice);
      inventoryItem = await prisma.inventoryItem.create({
        data: {
          productId,
          warehouseId,
          quantity: adjustedQuantity,
          availableQty: adjustedQuantity,
          averageCost: costPrice,
          totalValue: costPrice * adjustedQuantity,
        },
      });
    } else {
      const newQuantity = inventoryItem.quantity + adjustedQuantity;
      if (newQuantity < 0) {
        throw new AppError(400, 'Insufficient stock');
      }

      inventoryItem = await prisma.inventoryItem.update({
        where: { id: inventoryItem.id },
        data: {
          quantity: newQuantity,
        },
      });
    }

    // Create inventory movement record
    await prisma.inventoryMovement.create({
      data: {
        warehouseId,
        productId,
        type: type === 'ADD' ? 'IN' : 'OUT',
        quantity: Math.abs(quantity),
        unitCost: Number(product.costPrice),
        notes: `Stock ${type === 'ADD' ? 'addition' : 'removal'} by user`,
      },
    });

    await AuditLogger.log({
      entityType: 'INVENTORY',
      entityId: inventoryItem.id,
      action: 'STOCK_ADJUSTMENT',
      userId,
      changes: {
        type,
        quantity,
        productId,
        warehouseId,
      },
    });

    return inventoryItem;
  }

  async getHistory(id: string) {
    return AuditLogger.getEntityHistory('PRODUCT', id);
  }
}

export const productsService = new ProductsService();
