package repository

import (
	"context"
	"errors"
	"fmt"
	"time"

	"apskel-pos-be/internal/entities"
	"apskel-pos-be/internal/models"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

type InventoryRepository interface {
	Create(ctx context.Context, inventory *entities.Inventory) error
	GetByID(ctx context.Context, id uuid.UUID) (*entities.Inventory, error)
	GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.Inventory, error)
	GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID) (*entities.Inventory, error)
	GetByOutlet(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
	GetByProduct(ctx context.Context, productID uuid.UUID) ([]*entities.Inventory, error)
	GetLowStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
	GetZeroStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error)
	Update(ctx context.Context, inventory *entities.Inventory) error
	Delete(ctx context.Context, id uuid.UUID) error
	List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Inventory, int64, error)
	Count(ctx context.Context, filters map[string]interface{}) (int64, error)
	AdjustQuantity(ctx context.Context, productID, outletID uuid.UUID, delta int) (*entities.Inventory, error)
	SetQuantity(ctx context.Context, productID, outletID uuid.UUID, quantity int) (*entities.Inventory, error)
	UpdateReorderLevel(ctx context.Context, id uuid.UUID, reorderLevel int) error
	BulkCreate(ctx context.Context, inventoryItems []*entities.Inventory) error
	BulkUpdate(ctx context.Context, inventoryItems []*entities.Inventory) error
	BulkAdjustQuantity(ctx context.Context, adjustments map[uuid.UUID]int, outletID uuid.UUID) error
	GetTotalValueByOutlet(ctx context.Context, outletID uuid.UUID) (float64, error)
	GetInventoryReportSummary(ctx context.Context, outletID uuid.UUID, dateFrom, dateTo *time.Time) (*models.InventoryReportSummary, error)
	GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter) (*models.InventoryReportDetail, error)
}

type InventoryRepositoryImpl struct {
	db *gorm.DB
}

func NewInventoryRepositoryImpl(db *gorm.DB) *InventoryRepositoryImpl {
	return &InventoryRepositoryImpl{
		db: db,
	}
}

func (r *InventoryRepositoryImpl) Create(ctx context.Context, inventory *entities.Inventory) error {
	return r.db.WithContext(ctx).Create(inventory).Error
}

func (r *InventoryRepositoryImpl) GetByID(ctx context.Context, id uuid.UUID) (*entities.Inventory, error) {
	var inventory entities.Inventory
	err := r.db.WithContext(ctx).First(&inventory, "id = ?", id).Error
	if err != nil {
		return nil, err
	}
	return &inventory, nil
}

func (r *InventoryRepositoryImpl) GetWithRelations(ctx context.Context, id uuid.UUID) (*entities.Inventory, error) {
	var inventory entities.Inventory
	err := r.db.WithContext(ctx).
		Preload("Product").
		Preload("Product.Category").
		Preload("Outlet").
		First(&inventory, "id = ?", id).Error
	if err != nil {
		return nil, err
	}
	return &inventory, nil
}

func (r *InventoryRepositoryImpl) GetByProductAndOutlet(ctx context.Context, productID, outletID uuid.UUID) (*entities.Inventory, error) {
	var inventory entities.Inventory
	err := r.db.WithContext(ctx).Where("product_id = ? AND outlet_id = ?", productID, outletID).First(&inventory).Error
	if err != nil {
		return nil, err
	}
	return &inventory, nil
}

func (r *InventoryRepositoryImpl) GetByOutlet(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error) {
	var inventory []*entities.Inventory
	err := r.db.WithContext(ctx).
		Preload("Product").
		Preload("Product.Category").
		Where("outlet_id = ?", outletID).
		Find(&inventory).Error
	return inventory, err
}

func (r *InventoryRepositoryImpl) GetByProduct(ctx context.Context, productID uuid.UUID) ([]*entities.Inventory, error) {
	var inventory []*entities.Inventory
	err := r.db.WithContext(ctx).
		Preload("Outlet").
		Where("product_id = ?", productID).
		Find(&inventory).Error
	return inventory, err
}

func (r *InventoryRepositoryImpl) GetLowStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error) {
	var inventory []*entities.Inventory
	err := r.db.WithContext(ctx).
		Preload("Product").
		Preload("Product.Category").
		Where("outlet_id = ? AND quantity <= reorder_level", outletID).
		Find(&inventory).Error
	return inventory, err
}

func (r *InventoryRepositoryImpl) GetZeroStock(ctx context.Context, outletID uuid.UUID) ([]*entities.Inventory, error) {
	var inventory []*entities.Inventory
	err := r.db.WithContext(ctx).
		Preload("Product").
		Preload("Product.Category").
		Where("outlet_id = ? AND quantity = 0", outletID).
		Find(&inventory).Error
	return inventory, err
}

func (r *InventoryRepositoryImpl) Update(ctx context.Context, inventory *entities.Inventory) error {
	return r.db.WithContext(ctx).Save(inventory).Error
}

func (r *InventoryRepositoryImpl) Delete(ctx context.Context, id uuid.UUID) error {
	return r.db.WithContext(ctx).Delete(&entities.Inventory{}, "id = ?", id).Error
}

func (r *InventoryRepositoryImpl) List(ctx context.Context, filters map[string]interface{}, limit, offset int) ([]*entities.Inventory, int64, error) {
	var inventory []*entities.Inventory
	var total int64

	query := r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Preload("Product").
		Preload("Product.Category").
		Preload("Outlet")

	for key, value := range filters {
		switch key {
		case "search":
			searchValue := "%" + value.(string) + "%"
			query = query.Joins("JOIN products ON inventory.product_id = products.id").
				Where("products.name ILIKE ? OR products.sku ILIKE ?", searchValue, searchValue)
		case "low_stock":
			if value.(bool) {
				query = query.Where("quantity <= reorder_level")
			}
		case "zero_stock":
			if value.(bool) {
				query = query.Where("quantity = 0")
			}
		case "category_id":
			query = query.Joins("JOIN products ON inventory.product_id = products.id").
				Where("products.category_id = ?", value)
		default:
			query = query.Where(key+" = ?", value)
		}
	}

	if err := query.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	err := query.Limit(limit).Offset(offset).Find(&inventory).Error
	return inventory, total, err
}

func (r *InventoryRepositoryImpl) Count(ctx context.Context, filters map[string]interface{}) (int64, error) {
	var count int64
	query := r.db.WithContext(ctx).Model(&entities.Inventory{})

	for key, value := range filters {
		switch key {
		case "search":
			searchValue := "%" + value.(string) + "%"
			query = query.Joins("JOIN products ON inventory.product_id = products.id").
				Where("products.name ILIKE ? OR products.sku ILIKE ?", searchValue, searchValue)
		case "low_stock":
			if value.(bool) {
				query = query.Where("quantity <= reorder_level")
			}
		case "zero_stock":
			if value.(bool) {
				query = query.Where("quantity = 0")
			}
		case "category_id":
			query = query.Joins("JOIN products ON inventory.product_id = products.id").
				Where("products.category_id = ?", value)
		default:
			query = query.Where(key+" = ?", value)
		}
	}

	err := query.Count(&count).Error
	return count, err
}

func (r *InventoryRepositoryImpl) AdjustQuantity(ctx context.Context, productID, outletID uuid.UUID, delta int) (*entities.Inventory, error) {
	var inventory entities.Inventory

	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// Try to find existing inventory
		if err := tx.Where("product_id = ? AND outlet_id = ?", productID, outletID).First(&inventory).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				// Inventory doesn't exist, create it with initial quantity
				inventory = entities.Inventory{
					ProductID:    productID,
					OutletID:     outletID,
					Quantity:     0,
					ReorderLevel: 0,
				}
				if err := tx.Create(&inventory).Error; err != nil {
					return fmt.Errorf("failed to create inventory record: %w", err)
				}
			} else {
				return err
			}
		}

		inventory.UpdateQuantity(delta)
		return tx.Save(&inventory).Error
	})

	if err != nil {
		return nil, err
	}

	return &inventory, nil
}

func (r *InventoryRepositoryImpl) SetQuantity(ctx context.Context, productID, outletID uuid.UUID, quantity int) (*entities.Inventory, error) {
	var inventory entities.Inventory

	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		if err := tx.Where("product_id = ? AND outlet_id = ?", productID, outletID).First(&inventory).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				inventory = entities.Inventory{
					ProductID:    productID,
					OutletID:     outletID,
					Quantity:     quantity,
					ReorderLevel: 0,
				}
				if inventory.Quantity < 0 {
					inventory.Quantity = 0
				}
				if err := tx.Create(&inventory).Error; err != nil {
					return fmt.Errorf("failed to create inventory record: %w", err)
				}
				return nil
			}
			return err
		}

		// Set new quantity
		inventory.Quantity = quantity
		if inventory.Quantity < 0 {
			inventory.Quantity = 0
		}

		// Save updated inventory
		return tx.Save(&inventory).Error
	})

	if err != nil {
		return nil, err
	}

	return &inventory, nil
}

func (r *InventoryRepositoryImpl) UpdateReorderLevel(ctx context.Context, id uuid.UUID, reorderLevel int) error {
	return r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Where("id = ?", id).
		Update("reorder_level", reorderLevel).Error
}

func (r *InventoryRepositoryImpl) BulkCreate(ctx context.Context, inventoryItems []*entities.Inventory) error {
	return r.db.WithContext(ctx).CreateInBatches(inventoryItems, 100).Error
}

func (r *InventoryRepositoryImpl) BulkUpdate(ctx context.Context, inventoryItems []*entities.Inventory) error {
	if len(inventoryItems) == 0 {
		return nil
	}

	// Use GORM's transaction for bulk updates
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for _, inventory := range inventoryItems {
			if err := tx.Save(inventory).Error; err != nil {
				return fmt.Errorf("failed to update inventory for product %s: %w", inventory.ProductID, err)
			}
		}
		return nil
	})
}

func (r *InventoryRepositoryImpl) BulkAdjustQuantity(ctx context.Context, adjustments map[uuid.UUID]int, outletID uuid.UUID) error {
	return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for productID, delta := range adjustments {
			var inventory entities.Inventory
			if err := tx.Where("product_id = ? AND outlet_id = ?", productID, outletID).First(&inventory).Error; err != nil {
				if errors.Is(err, gorm.ErrRecordNotFound) {
					// Inventory doesn't exist, create it with initial quantity
					inventory = entities.Inventory{
						ProductID:    productID,
						OutletID:     outletID,
						Quantity:     0,
						ReorderLevel: 0,
					}
					if err := tx.Create(&inventory).Error; err != nil {
						return fmt.Errorf("failed to create inventory record for product %s: %w", productID, err)
					}
				} else {
					return err
				}
			}

			inventory.UpdateQuantity(delta)

			if err := tx.Save(&inventory).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

func (r *InventoryRepositoryImpl) GetTotalValueByOutlet(ctx context.Context, outletID uuid.UUID) (float64, error) {
	var totalValue float64
	if err := r.db.WithContext(ctx).
		Table("inventory").
		Select("SUM(inventory.quantity * products.cost)").
		Joins("JOIN products ON inventory.product_id = products.id").
		Where("inventory.outlet_id = ?", outletID).
		Scan(&totalValue).Error; err != nil {
		return 0, fmt.Errorf("failed to get total value: %w", err)
	}

	return totalValue, nil
}

// GetInventoryReportSummary returns summary statistics for inventory report
func (r *InventoryRepositoryImpl) GetInventoryReportSummary(ctx context.Context, outletID uuid.UUID, dateFrom, dateTo *time.Time) (*models.InventoryReportSummary, error) {
	var summary models.InventoryReportSummary
	summary.OutletID = outletID
	summary.GeneratedAt = time.Now()

	// Get outlet name
	var outlet entities.Outlet
	if err := r.db.WithContext(ctx).Select("name").First(&outlet, "id = ?", outletID).Error; err != nil {
		return nil, fmt.Errorf("failed to get outlet name: %w", err)
	}
	summary.OutletName = outlet.Name

	var totalProducts int64
	if err := r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Joins("JOIN products ON inventory.product_id = products.id").
		Where("inventory.outlet_id = ?", outletID).
		Count(&totalProducts).Error; err != nil {
		return nil, fmt.Errorf("failed to count total products: %w", err)
	}
	summary.TotalProducts = int(totalProducts)

	var totalIngredients int64
	if err := r.db.WithContext(ctx).Model(&entities.Ingredient{}).
		Where("outlet_id = ? AND is_active = ?", outletID, true).
		Count(&totalIngredients).Error; err != nil {
		return nil, fmt.Errorf("failed to count total ingredients: %w", err)
	}
	summary.TotalIngredients = int(totalIngredients)

	var lowStockProducts int64
	if err := r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Joins("JOIN products ON inventory.product_id = products.id").
		Where("inventory.outlet_id = ? AND inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0", outletID).
		Count(&lowStockProducts).Error; err != nil {
		return nil, fmt.Errorf("failed to count low stock products: %w", err)
	}
	summary.LowStockProducts = int(lowStockProducts)

	var lowStockIngredients int64
	if err := r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Joins("JOIN ingredients ON inventory.product_id = ingredients.id").
		Where("inventory.outlet_id = ? AND inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0", outletID).
		Count(&lowStockIngredients).Error; err != nil {
		return nil, fmt.Errorf("failed to count low stock ingredients: %w", err)
	}
	summary.LowStockIngredients = int(lowStockIngredients)

	var zeroStockProducts int64
	if err := r.db.WithContext(ctx).Model(&entities.Inventory{}).
		Joins("JOIN products ON inventory.product_id = products.id").
		Where("inventory.outlet_id = ? AND inventory.quantity = 0", outletID).
		Count(&zeroStockProducts).Error; err != nil {
		return nil, fmt.Errorf("failed to count zero stock products: %w", err)
	}
	summary.ZeroStockProducts = int(zeroStockProducts)

	var zeroStockIngredients int64
	if err := r.db.WithContext(ctx).Model(&entities.Ingredient{}).
		Where("outlet_id = ? AND is_active = ? AND stock = 0", outletID, true).
		Count(&zeroStockIngredients).Error; err != nil {
		return nil, fmt.Errorf("failed to count zero stock ingredients: %w", err)
	}
	summary.ZeroStockIngredients = int(zeroStockIngredients)

	// Get total sold from inventory movements
	totalSoldProducts, totalSoldIngredients, err := r.getTotalSoldFromMovements(ctx, outletID, dateFrom, dateTo)
	if err != nil {
		return nil, fmt.Errorf("failed to get total sold from movements: %w", err)
	}
	summary.TotalSoldProducts = totalSoldProducts
	summary.TotalSoldIngredients = totalSoldIngredients

	totalValue, err := r.GetTotalValueByOutlet(ctx, outletID)
	if err != nil {
		return nil, fmt.Errorf("failed to get total value: %w", err)
	}
	summary.TotalValue = totalValue

	return &summary, nil
}

// GetInventoryReportDetails returns detailed inventory report with products and ingredients
func (r *InventoryRepositoryImpl) GetInventoryReportDetails(ctx context.Context, filter *models.InventoryReportFilter) (*models.InventoryReportDetail, error) {
	report := &models.InventoryReportDetail{}

	if filter.OutletID != nil {
		summary, err := r.GetInventoryReportSummary(ctx, *filter.OutletID, filter.DateFrom, filter.DateTo)
		if err != nil {
			return nil, fmt.Errorf("failed to get report summary: %w", err)
		}
		report.Summary = summary
	}

	products, err := r.getInventoryProductsDetails(ctx, filter)
	if err != nil {
		return nil, fmt.Errorf("failed to get products details: %w", err)
	}
	report.Products = products

	ingredients, err := r.getInventoryIngredientsDetails(ctx, filter)
	if err != nil {
		return nil, fmt.Errorf("failed to get ingredients details: %w", err)
	}
	report.Ingredients = ingredients

	return report, nil
}

// getInventoryProductsDetails retrieves detailed product inventory information
func (r *InventoryRepositoryImpl) getInventoryProductsDetails(ctx context.Context, filter *models.InventoryReportFilter) ([]*models.InventoryProductDetail, error) {
	// Build the base query
	query := r.db.WithContext(ctx).Table("inventory").
		Select(`
			inventory.id,
			inventory.product_id,
			products.name as product_name,
			categories.name as category_name,
			inventory.quantity,
			inventory.reorder_level,
			COALESCE(product_variants.cost, products.cost) as unit_cost,
			(COALESCE(product_variants.cost, products.cost) * inventory.quantity) as total_value,
			inventory.updated_at
		`).
		Joins("JOIN products ON inventory.product_id = products.id").
		Joins("LEFT JOIN categories ON products.category_id = categories.id").
		Joins("LEFT JOIN product_variants ON products.id = product_variants.product_id").
		Where("inventory.outlet_id = ?", filter.OutletID)

	// Apply filters
	if filter.CategoryID != nil {
		query = query.Where("products.category_id = ?", *filter.CategoryID)
	}
	if filter.ShowLowStock != nil && *filter.ShowLowStock {
		query = query.Where("inventory.quantity <= inventory.reorder_level AND inventory.quantity > 0")
	}
	if filter.ShowZeroStock != nil && *filter.ShowZeroStock {
		query = query.Where("inventory.quantity = 0")
	}
	if filter.Search != nil && *filter.Search != "" {
		searchTerm := "%" + *filter.Search + "%"
		query = query.Where("products.name ILIKE ? OR categories.name ILIKE ?", searchTerm, searchTerm)
	}

	// Apply pagination
	if filter.Limit != nil {
		query = query.Limit(*filter.Limit)
	}
	if filter.Offset != nil {
		query = query.Offset(*filter.Offset)
	}

	query = query.Order("products.name ASC")

	// Execute the base query first
	var baseResults []struct {
		ID           uuid.UUID
		ProductID    uuid.UUID
		ProductName  string
		CategoryName *string
		Quantity     int
		ReorderLevel int
		UnitCost     float64
		TotalValue   float64
		UpdatedAt    time.Time
	}

	if err := query.Find(&baseResults).Error; err != nil {
		return nil, err
	}

	// Now get total in and out for each product
	var products []*models.InventoryProductDetail
	for _, result := range baseResults {
		var totalIn, totalOut float64

		// Query total in (positive movements) for this specific product
		inQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
			Select("COALESCE(SUM(quantity), 0)").
			Where("item_id = ? AND outlet_id = ? AND item_type = ? AND quantity > 0",
				result.ProductID, filter.OutletID, "PRODUCT")

		// Query total out (negative movements) for this specific product
		outQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
			Select("COALESCE(SUM(ABS(quantity)), 0)").
			Where("item_id = ? AND outlet_id = ? AND item_type = ? AND quantity < 0",
				result.ProductID, filter.OutletID, "PRODUCT")

		// Apply date filters if provided
		if filter.DateFrom != nil {
			inQuery = inQuery.Where("created_at >= ?", *filter.DateFrom)
			outQuery = outQuery.Where("created_at >= ?", *filter.DateFrom)
		}
		if filter.DateTo != nil {
			inQuery = inQuery.Where("created_at <= ?", *filter.DateTo)
			outQuery = outQuery.Where("created_at <= ?", *filter.DateTo)
		}

		if err := inQuery.Scan(&totalIn).Error; err != nil {
			return nil, fmt.Errorf("failed to get total in for product %s: %w", result.ProductID, err)
		}

		if err := outQuery.Scan(&totalOut).Error; err != nil {
			return nil, fmt.Errorf("failed to get total out for product %s: %w", result.ProductID, err)
		}

		categoryName := ""
		if result.CategoryName != nil {
			categoryName = *result.CategoryName
		}

		product := &models.InventoryProductDetail{
			ID:           result.ID,
			ProductID:    result.ProductID,
			ProductName:  result.ProductName,
			CategoryName: categoryName,
			Quantity:     result.Quantity,
			ReorderLevel: result.ReorderLevel,
			UnitCost:     result.UnitCost,
			TotalValue:   result.TotalValue,
			TotalIn:      totalIn,
			TotalOut:     totalOut,
			IsLowStock:   result.Quantity <= result.ReorderLevel && result.Quantity > 0,
			IsZeroStock:  result.Quantity == 0,
			UpdatedAt:    result.UpdatedAt,
		}
		products = append(products, product)
	}

	return products, nil
}

// getInventoryIngredientsDetails retrieves detailed ingredient inventory information
func (r *InventoryRepositoryImpl) getInventoryIngredientsDetails(ctx context.Context, filter *models.InventoryReportFilter) ([]*models.InventoryIngredientDetail, error) {
	query := r.db.WithContext(ctx).Table("ingredients").
		Select(`
			ingredients.id,
			ingredients.id as ingredient_id,
			ingredients.name as ingredient_name,
			units.name as unit_name,
			ingredients.stock as quantity,
			0 as reorder_level,
			ingredients.cost as unit_cost,
			(ingredients.cost * ingredients.stock) as total_value,
			ingredients.updated_at
		`).
		Joins("LEFT JOIN units ON ingredients.unit_id = units.id").
		Where("ingredients.outlet_id = ? AND ingredients.is_active = ?", filter.OutletID, true)

	if filter.ShowLowStock != nil && *filter.ShowLowStock {
		query = query.Where("ingredients.stock <= 0 AND ingredients.stock > 0")
	}
	if filter.ShowZeroStock != nil && *filter.ShowZeroStock {
		query = query.Where("ingredients.stock = 0")
	}
	if filter.Search != nil && *filter.Search != "" {
		searchTerm := "%" + *filter.Search + "%"
		query = query.Where("ingredients.name ILIKE ? OR units.name ILIKE ?", searchTerm, searchTerm)
	}

	// Apply pagination
	if filter.Limit != nil {
		query = query.Limit(*filter.Limit)
	}
	if filter.Offset != nil {
		query = query.Offset(*filter.Offset)
	}

	query = query.Order("ingredients.name ASC")

	var baseResults []struct {
		ID             uuid.UUID
		IngredientID   uuid.UUID
		IngredientName string
		UnitName       *string
		Quantity       float64
		ReorderLevel   int
		UnitCost       float64
		TotalValue     float64
		UpdatedAt      time.Time
	}

	if err := query.Find(&baseResults).Error; err != nil {
		return nil, err
	}

	var ingredients []*models.InventoryIngredientDetail
	for _, result := range baseResults {
		var totalIn, totalOut float64

		// Query total in (positive movements) for this specific ingredient
		inQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
			Select("COALESCE(SUM(quantity), 0)").
			Where("item_id = ? AND outlet_id = ? AND item_type = ? AND quantity > 0",
				result.IngredientID, filter.OutletID, "INGREDIENT")

		// Query total out (negative movements) for this specific ingredient
		outQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
			Select("COALESCE(SUM(ABS(quantity)), 0)").
			Where("item_id = ? AND outlet_id = ? AND item_type = ? AND quantity < 0",
				result.IngredientID, filter.OutletID, "INGREDIENT")

		if filter.DateFrom != nil {
			inQuery = inQuery.Where("created_at >= ?", *filter.DateFrom)
			outQuery = outQuery.Where("created_at >= ?", *filter.DateFrom)
		}
		if filter.DateTo != nil {
			inQuery = inQuery.Where("created_at <= ?", *filter.DateTo)
			outQuery = outQuery.Where("created_at <= ?", *filter.DateTo)
		}

		if err := inQuery.Scan(&totalIn).Error; err != nil {
			return nil, fmt.Errorf("failed to get total in for ingredient %s: %w", result.IngredientID, err)
		}

		if err := outQuery.Scan(&totalOut).Error; err != nil {
			return nil, fmt.Errorf("failed to get total out for ingredient %s: %w", result.IngredientID, err)
		}

		unitName := ""
		if result.UnitName != nil {
			unitName = *result.UnitName
		}

		ingredient := &models.InventoryIngredientDetail{
			ID:             result.ID,
			IngredientID:   result.IngredientID,
			IngredientName: result.IngredientName,
			UnitName:       unitName,
			Quantity:       int(result.Quantity),
			ReorderLevel:   result.ReorderLevel,
			UnitCost:       result.UnitCost,
			TotalValue:     result.TotalValue,
			TotalIn:        totalIn,
			TotalOut:       totalOut,
			IsLowStock:     result.Quantity <= 0 && result.Quantity > 0,
			IsZeroStock:    result.Quantity == 0,
			UpdatedAt:      result.UpdatedAt,
		}
		ingredients = append(ingredients, ingredient)
	}

	return ingredients, nil
}

// getTotalSoldFromMovements calculates total sold quantities from inventory movements
func (r *InventoryRepositoryImpl) getTotalSoldFromMovements(ctx context.Context, outletID uuid.UUID, dateFrom, dateTo *time.Time) (float64, float64, error) {
	var totalSoldProducts float64
	var totalSoldIngredients float64

	// Build base query for products
	productQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
		Select("COALESCE(SUM(ABS(quantity)), 0)").
		Where("outlet_id = ? AND item_type = ? AND movement_type = ? AND quantity < 0",
			outletID, "PRODUCT", entities.InventoryMovementTypeSale)

	// Build base query for ingredients
	ingredientQuery := r.db.WithContext(ctx).Model(&entities.InventoryMovement{}).
		Select("COALESCE(SUM(ABS(quantity)), 0)").
		Where("outlet_id = ? AND item_type = ? AND movement_type = ? AND quantity < 0",
			outletID, "INGREDIENT", entities.InventoryMovementTypeSale)

	// Apply date range filters if provided
	if dateFrom != nil {
		productQuery = productQuery.Where("created_at >= ?", *dateFrom)
		ingredientQuery = ingredientQuery.Where("created_at >= ?", *dateFrom)
	}
	if dateTo != nil {
		productQuery = productQuery.Where("created_at <= ?", *dateTo)
		ingredientQuery = ingredientQuery.Where("created_at <= ?", *dateTo)
	}

	// Get total sold products from inventory movements
	if err := productQuery.Scan(&totalSoldProducts).Error; err != nil {
		return 0, 0, fmt.Errorf("failed to get total sold products: %w", err)
	}

	// Get total sold ingredients from inventory movements
	if err := ingredientQuery.Scan(&totalSoldIngredients).Error; err != nil {
		return 0, 0, fmt.Errorf("failed to get total sold ingredients: %w", err)
	}

	return totalSoldProducts, totalSoldIngredients, nil
}
