package repository

import (
	"context"
	"sort"
	"strings"

	commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
	"gorm.io/gorm"
)

type MarketingDeliveryProductRepository interface {
	commonRepo.BaseRepository[entity.MarketingDeliveryProduct]
	GetDeliveryProductsByProjectFlockID(ctx context.Context, projectFlockID uint, callback func(*gorm.DB) *gorm.DB) ([]entity.MarketingDeliveryProduct, error)
	GetClosingPenjualan(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error)
	GetClosingPenjualanByCategory(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint, category string) ([]entity.MarketingDeliveryProduct, error)
	GetByMarketingId(ctx context.Context, marketingId uint) ([]entity.MarketingDeliveryProduct, error)
	GetByMarketingProductID(ctx context.Context, marketingProductID uint) (*entity.MarketingDeliveryProduct, error)
	GetAllWithFilters(ctx context.Context, offset, limit int, filters *validation.MarketingQuery) ([]entity.MarketingDeliveryProduct, int64, error)
	UpdateFifoFields(ctx context.Context, id uint, usageQty, pendingQty float64) error
	GetUsageQty(ctx context.Context, id uint) (float64, error)
	ResetFifoFields(ctx context.Context, id uint) error
	GetClosingPenjualanForAgeChickDataProduction(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error)
	GetAttributionRowsByDeliveryProductIDs(ctx context.Context, deliveryProductIDs []uint) ([]commonRepo.MarketingDeliveryAttributionRow, error)
}

type MarketingDeliveryProductRepositoryImpl struct {
	*commonRepo.BaseRepositoryImpl[entity.MarketingDeliveryProduct]
}

const marketingDeliveryProductSelectWithNullAttributed = "marketing_delivery_products.*, NULL AS attributed_project_flock_kandang_id"

func NewMarketingDeliveryProductRepository(db *gorm.DB) MarketingDeliveryProductRepository {
	return &MarketingDeliveryProductRepositoryImpl{
		BaseRepositoryImpl: commonRepo.NewBaseRepository[entity.MarketingDeliveryProduct](db),
	}
}

func (r *MarketingDeliveryProductRepositoryImpl) GetDeliveryProductsByProjectFlockID(ctx context.Context, projectFlockID uint, callback func(*gorm.DB) *gorm.DB) ([]entity.MarketingDeliveryProduct, error) {
	var deliveryProducts []entity.MarketingDeliveryProduct

	attributionQuery := commonRepo.MarketingDeliveryAttributionRowsQuery(r.DB().WithContext(ctx))

	db := r.DB().WithContext(ctx).
		Select("DISTINCT "+marketingDeliveryProductSelectWithNullAttributed).
		Joins("JOIN (?) AS mda ON mda.marketing_delivery_product_id = marketing_delivery_products.id", attributionQuery).
		Where("mda.project_flock_id = ?", projectFlockID)

	if callback != nil {
		db = callback(db)
	}

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

	return deliveryProducts, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualan(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error) {
	attributionRows, err := r.getClosingAttributionRows(ctx, projectFlockID, projectFlockKandangID, nil)
	if err != nil {
		return nil, err
	}
	return r.fetchClosingDeliveryProducts(ctx, attributionRows, projectFlockKandangID)
}

func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanForAgeChickDataProduction(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]entity.MarketingDeliveryProduct, error) {
	attributionRows, err := r.getClosingAttributionRows(ctx, projectFlockID, projectFlockKandangID, []string{
		string(utils.FlagAyamAfkir),
		string(utils.FlagAyamCulling),
		string(utils.FlagPullet),
		string(utils.FlagLayer),
	})
	if err != nil {
		return nil, err
	}
	return r.fetchClosingDeliveryProducts(ctx, attributionRows, projectFlockKandangID)
}

func (r *MarketingDeliveryProductRepositoryImpl) GetClosingPenjualanByCategory(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint, category string) ([]entity.MarketingDeliveryProduct, error) {
	flagNames := []string{
		string(utils.FlagDOC),
		string(utils.FlagPullet),
		string(utils.FlagLayer),
		string(utils.FlagAyamAfkir),
		string(utils.FlagAyamCulling),
		string(utils.FlagAyamMati),
	}
	if category == string(utils.ProjectFlockCategoryLaying) {
		flagNames = []string{
			string(utils.FlagTelur),
			string(utils.FlagTelurUtuh),
			string(utils.FlagTelurPecah),
			string(utils.FlagTelurPutih),
			string(utils.FlagTelurRetak),
		}
	}

	attributionRows, err := r.getClosingAttributionRows(ctx, projectFlockID, projectFlockKandangID, flagNames)
	if err != nil {
		return nil, err
	}
	return r.fetchClosingDeliveryProducts(ctx, attributionRows, projectFlockKandangID)
}

func (r *MarketingDeliveryProductRepositoryImpl) GetByMarketingId(ctx context.Context, marketingId uint) ([]entity.MarketingDeliveryProduct, error) {
	var deliveryProducts []entity.MarketingDeliveryProduct

	// JOIN untuk filter by marketing_id yang ada di related table
	db := r.DB().WithContext(ctx).
		Select(marketingDeliveryProductSelectWithNullAttributed).
		Joins("JOIN marketing_products ON marketing_products.id = marketing_delivery_products.marketing_product_id").
		Where("marketing_products.marketing_id = ?", marketingId)

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

	return deliveryProducts, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) GetByMarketingProductID(ctx context.Context, marketingProductID uint) (*entity.MarketingDeliveryProduct, error) {
	var deliveryProduct entity.MarketingDeliveryProduct

	if err := r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Select(marketingDeliveryProductSelectWithNullAttributed).
		Where("marketing_product_id = ?", marketingProductID).
		First(&deliveryProduct).Error; err != nil {
		return nil, err
	}

	return &deliveryProduct, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) GetByID(
	ctx context.Context,
	id uint,
	modifier func(*gorm.DB) *gorm.DB,
) (*entity.MarketingDeliveryProduct, error) {
	var deliveryProduct entity.MarketingDeliveryProduct

	q := r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Select(marketingDeliveryProductSelectWithNullAttributed)
	if modifier != nil {
		q = modifier(q)
	}

	if err := q.First(&deliveryProduct, id).Error; err != nil {
		return nil, err
	}

	return &deliveryProduct, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) GetAttributionRowsByDeliveryProductIDs(ctx context.Context, deliveryProductIDs []uint) ([]commonRepo.MarketingDeliveryAttributionRow, error) {
	if len(deliveryProductIDs) == 0 {
		return []commonRepo.MarketingDeliveryAttributionRow{}, nil
	}

	var rows []commonRepo.MarketingDeliveryAttributionRow
	query := r.DB().WithContext(ctx).
		Table("(?) AS mda", commonRepo.MarketingDeliveryAttributionRowsQuery(r.DB().WithContext(ctx))).
		Where("mda.marketing_delivery_product_id IN ?", deliveryProductIDs).
		Order("mda.marketing_delivery_product_id ASC, mda.project_flock_kandang_id ASC")

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

	return rows, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) getClosingAttributionRows(
	ctx context.Context,
	projectFlockID uint,
	projectFlockKandangID *uint,
	flagNames []string,
) ([]commonRepo.MarketingDeliveryAttributionRow, error) {
	var rows []commonRepo.MarketingDeliveryAttributionRow

	attributionQuery := commonRepo.MarketingDeliveryAttributionRowsQuery(r.DB().WithContext(ctx))
	query := r.DB().WithContext(ctx).
		Table("(?) AS mda", attributionQuery).
		Joins("JOIN marketing_delivery_products mdp ON mdp.id = mda.marketing_delivery_product_id").
		Joins("JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
		Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id").
		Joins("JOIN products prod ON prod.id = pw.product_id").
		Where("mda.project_flock_id = ?", projectFlockID).
		Where("mdp.delivery_date IS NOT NULL")

	if projectFlockKandangID != nil {
		query = query.Where("mda.project_flock_kandang_id = ?", *projectFlockKandangID)
	}
	if len(flagNames) > 0 {
		query = query.
			Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
			Where("f.name IN ?", flagNames)
	}

	query = query.
		Select(`
			mda.marketing_delivery_product_id,
			mda.project_flock_kandang_id,
			mda.project_flock_id,
			mda.project_flock_category,
			SUM(mda.allocated_qty) AS allocated_qty
		`).
		Group(`
			mda.marketing_delivery_product_id,
			mda.project_flock_kandang_id,
			mda.project_flock_id,
			mda.project_flock_category
		`).
		Order("mda.marketing_delivery_product_id ASC, mda.project_flock_kandang_id ASC")

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

	return rows, nil
}

func (r *MarketingDeliveryProductRepositoryImpl) fetchClosingDeliveryProducts(
	ctx context.Context,
	attributionRows []commonRepo.MarketingDeliveryAttributionRow,
	projectFlockKandangID *uint,
) ([]entity.MarketingDeliveryProduct, error) {
	deliveryIDs := orderedDeliveryProductIDs(attributionRows)
	if len(deliveryIDs) == 0 {
		return []entity.MarketingDeliveryProduct{}, nil
	}

	query := r.closingDeliveryProductsQuery(ctx).
		Select(marketingDeliveryProductSelectWithNullAttributed).
		Where("marketing_delivery_products.id IN ?", deliveryIDs).
		Order("marketing_delivery_products.delivery_date DESC")

	if projectFlockKandangID == nil {
		query = query.Joins(
			"LEFT JOIN (?) AS mda_single ON mda_single.marketing_delivery_product_id = marketing_delivery_products.id",
			commonRepo.MarketingDeliverySingleAttributionQuery(r.DB().WithContext(ctx)),
		).Select("marketing_delivery_products.*, mda_single.attributed_project_flock_kandang_id")
	}

	var deliveryProducts []entity.MarketingDeliveryProduct
	if err := query.Find(&deliveryProducts).Error; err != nil {
		return nil, err
	}

	if projectFlockKandangID == nil {
		return deliveryProducts, nil
	}

	return scaleDeliveryProductsByAttribution(deliveryProducts, attributionRows, *projectFlockKandangID), nil
}

func (r *MarketingDeliveryProductRepositoryImpl) closingDeliveryProductsQuery(ctx context.Context) *gorm.DB {
	return r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Preload("MarketingProduct").
		Preload("MarketingProduct.ProductWarehouse").
		Preload("MarketingProduct.ProductWarehouse.Product").
		Preload("MarketingProduct.ProductWarehouse.Product.ProductCategory").
		Preload("MarketingProduct.ProductWarehouse.Product.Uom").
		Preload("MarketingProduct.ProductWarehouse.Product.Flags").
		Preload("MarketingProduct.ProductWarehouse.Warehouse").
		Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang").
		Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang.ProjectFlock").
		Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang.Kandang").
		Preload("MarketingProduct.ProductWarehouse.ProjectFlockKandang.Chickins").
		Preload("MarketingProduct.Marketing").
		Preload("MarketingProduct.Marketing.Customer").
		Preload("AttributedProjectFlockKandang").
		Preload("AttributedProjectFlockKandang.ProjectFlock").
		Preload("AttributedProjectFlockKandang.Kandang").
		Preload("AttributedProjectFlockKandang.Chickins")
}

func orderedDeliveryProductIDs(rows []commonRepo.MarketingDeliveryAttributionRow) []uint {
	seen := make(map[uint]struct{}, len(rows))
	ids := make([]uint, 0, len(rows))
	for _, row := range rows {
		if row.MarketingDeliveryProductID == 0 {
			continue
		}
		if _, ok := seen[row.MarketingDeliveryProductID]; ok {
			continue
		}
		seen[row.MarketingDeliveryProductID] = struct{}{}
		ids = append(ids, row.MarketingDeliveryProductID)
	}
	sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
	return ids
}

func scaleDeliveryProductsByAttribution(
	deliveryProducts []entity.MarketingDeliveryProduct,
	rows []commonRepo.MarketingDeliveryAttributionRow,
	projectFlockKandangID uint,
) []entity.MarketingDeliveryProduct {
	if len(deliveryProducts) == 0 || projectFlockKandangID == 0 {
		return deliveryProducts
	}

	totalByDelivery := make(map[uint]float64, len(rows))
	selectedByDelivery := make(map[uint]float64, len(rows))
	for _, row := range rows {
		totalByDelivery[row.MarketingDeliveryProductID] += row.AllocatedQty
		if row.ProjectFlockKandangID == projectFlockKandangID {
			selectedByDelivery[row.MarketingDeliveryProductID] += row.AllocatedQty
		}
	}

	filtered := make([]entity.MarketingDeliveryProduct, 0, len(deliveryProducts))
	for _, delivery := range deliveryProducts {
		selectedQty := selectedByDelivery[delivery.Id]
		totalQty := totalByDelivery[delivery.Id]
		if selectedQty <= 0 {
			continue
		}

		share := 1.0
		if totalQty > 0 {
			share = selectedQty / totalQty
		}

		cloned := delivery
		cloned.AttributedProjectFlockKandangId = &projectFlockKandangID
		cloned.UsageQty = selectedQty
		cloned.PendingQty = 0
		cloned.TotalWeight = delivery.TotalWeight * share
		cloned.TotalPrice = delivery.TotalPrice * share
		filtered = append(filtered, cloned)
	}

	return filtered
}

func (r *MarketingDeliveryProductRepositoryImpl) GetAllWithFilters(ctx context.Context, offset, limit int, filters *validation.MarketingQuery) ([]entity.MarketingDeliveryProduct, int64, error) {
	var deliveryProducts []entity.MarketingDeliveryProduct
	var total int64

	baseDB := r.DB().WithContext(ctx)
	singleAttributionQuery := commonRepo.MarketingDeliverySingleAttributionQuery(baseDB)
	db := r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Select("marketing_delivery_products.*, mda_single.attributed_project_flock_kandang_id").
		Joins("LEFT JOIN (?) AS mda_single ON mda_single.marketing_delivery_product_id = marketing_delivery_products.id", singleAttributionQuery).
		Preload("MarketingProduct", func(db *gorm.DB) *gorm.DB {
			return db.
				Preload("Marketing").
				Preload("Marketing.Customer").
				Preload("Marketing.SalesPerson").
				Preload("ProductWarehouse").
				Preload("ProductWarehouse.Product").
				Preload("ProductWarehouse.Product.Flags").
				Preload("ProductWarehouse.Warehouse").
				Preload("ProductWarehouse.ProjectFlockKandang").
				Preload("ProductWarehouse.ProjectFlockKandang.ProjectFlock")
		}).
		Preload("AttributedProjectFlockKandang").
		Preload("AttributedProjectFlockKandang.ProjectFlock").
		Preload("AttributedProjectFlockKandang.Kandang").
		Joins("JOIN marketing_products ON marketing_products.id = marketing_delivery_products.marketing_product_id").
		Joins("JOIN marketings ON marketings.id = marketing_products.marketing_id").
		Where("marketing_delivery_products.delivery_date IS NOT NULL")

	if filters.ProductId > 0 || filters.WarehouseId > 0 || filters.AreaId > 0 || filters.LocationId > 0 || filters.AllowedAreaIDs != nil || filters.AllowedLocationIDs != nil || filters.Search != "" || filters.MarketingType != "" {
		db = db.Joins("LEFT JOIN product_warehouses ON product_warehouses.id = marketing_products.product_warehouse_id")
	}

	if filters.ProductId > 0 || filters.Search != "" || filters.MarketingType != "" {
		db = db.Joins("LEFT JOIN products ON products.id = product_warehouses.product_id")
	}

	if filters.WarehouseId > 0 || filters.Search != "" {
		db = db.Joins("LEFT JOIN warehouses ON warehouses.id = product_warehouses.warehouse_id")
	}

	if filters.Search != "" {
		db = db.Joins("LEFT JOIN customers ON customers.id = marketings.customer_id")
		db = db.Joins("LEFT JOIN users AS sales_users ON sales_users.id = marketings.sales_person_id")

		searchPattern := "%" + filters.Search + "%"
		db = db.Where(`(
			marketing_delivery_products.vehicle_number ILIKE ? OR
			customers.name ILIKE ? OR
			warehouses.name ILIKE ? OR
			products.name ILIKE ? OR
			sales_users.name ILIKE ? OR
			CONCAT(
				marketings.so_number,
				'-',
				COALESCE(TO_CHAR(marketing_delivery_products.delivery_date, 'YYYYMMDD'), ''),
				'-',
				COALESCE(product_warehouses.warehouse_id::text, '')
			) ILIKE ?
		)`,
			searchPattern, searchPattern, searchPattern, searchPattern, searchPattern, searchPattern)
	}

	if filters.CustomerId > 0 {
		db = db.Where("marketings.customer_id = ?", filters.CustomerId)
	}

	if filters.SalesPersonId > 0 {
		db = db.Where("marketings.sales_person_id = ?", filters.SalesPersonId)
	}

	if filters.ProductId > 0 {
		db = db.Where("product_warehouses.product_id = ?", filters.ProductId)
	}

	if filters.WarehouseId > 0 {
		db = db.Where("product_warehouses.warehouse_id = ?", filters.WarehouseId)
	}

	if filters.AreaId > 0 || filters.LocationId > 0 || filters.AllowedAreaIDs != nil || filters.AllowedLocationIDs != nil {
		buildAttrFilter := func() *gorm.DB {
			return r.DB().WithContext(ctx).
				Table("(?) AS mda", commonRepo.MarketingDeliveryAttributionRowsQuery(r.DB().WithContext(ctx))).
				Select("1").
				Joins("JOIN project_flock_kandangs pfk ON pfk.id = mda.project_flock_kandang_id").
				Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
				Where("mda.marketing_delivery_product_id = marketing_delivery_products.id")
		}
		if filters.AreaId > 0 {
			db = db.Where("EXISTS (?)", buildAttrFilter().Where("pf.area_id = ?", filters.AreaId))
		}

		if filters.LocationId > 0 {
			db = db.Where("EXISTS (?)", buildAttrFilter().Where("pf.location_id = ?", filters.LocationId))
		}

		if filters.AllowedAreaIDs != nil {
			if len(filters.AllowedAreaIDs) == 0 {
				db = db.Where("1 = 0")
			} else {
				db = db.Where("EXISTS (?)", buildAttrFilter().Where("pf.area_id IN ?", filters.AllowedAreaIDs))
			}
		}

		if filters.AllowedLocationIDs != nil {
			if len(filters.AllowedLocationIDs) == 0 {
				db = db.Where("1 = 0")
			} else {
				db = db.Where("EXISTS (?)", buildAttrFilter().Where("pf.location_id IN ?", filters.AllowedLocationIDs))
			}
		}
	}

	if filters.MarketingType != "" {
		db = db.Joins("LEFT JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = 'products'").
			Group("marketing_delivery_products.id")

		switch filters.MarketingType {
		case "ayam":
			db = db.Where("flags.name IN (?)", []string{
				string(utils.FlagDOC), string(utils.FlagPullet), string(utils.FlagLayer),
			})
		case "telur":
			db = db.Where("flags.name IN (?)", []string{
				string(utils.FlagTelur), string(utils.FlagTelurUtuh), string(utils.FlagTelurPecah),
				string(utils.FlagTelurPutih), string(utils.FlagTelurRetak),
			})
		case "trading":
			db = db.Where("flags.name IN (?)", []string{
				string(utils.FlagOVK), string(utils.FlagObat), string(utils.FlagVitamin), string(utils.FlagKimia),
				string(utils.FlagPakan), string(utils.FlagPreStarter), string(utils.FlagStarter), string(utils.FlagFinisher),
			})
		}
	}

	if filters.StartDate != "" || filters.EndDate != "" {
		filterBy := filters.FilterBy
		if filterBy == "" {
			filterBy = "so_date"
		}
		if filterBy == "so_date" {
			if filters.StartDate != "" {
				if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
					db = db.Where("marketings.so_date >= ?", startDate)
				}
			}
			if filters.EndDate != "" {
				if endDate, err := utils.ParseDateString(filters.EndDate); err == nil {
					nextDate := endDate.AddDate(0, 0, 1)
					db = db.Where("marketings.so_date < ?", nextDate)
				}
			}
		} else if filterBy == "realization_date" {
			if filters.StartDate != "" {
				if startDate, err := utils.ParseDateString(filters.StartDate); err == nil {
					db = db.Where("marketing_delivery_products.delivery_date >= ?", startDate)
				}
			}
			if filters.EndDate != "" {
				if endDate, err := utils.ParseDateString(filters.EndDate); err == nil {
					nextDate := endDate.AddDate(0, 0, 1)
					db = db.Where("marketing_delivery_products.delivery_date < ?", nextDate)
				}
			}
		}
	}

	sortColumn := "marketing_delivery_products.id"
	sortOrder := "DESC"

	if filters.SortBy != "" {
		switch filters.SortBy {
		case "so_date":
			sortColumn = "marketings.so_date"
		case "realization_date":
			sortColumn = "marketing_delivery_products.delivery_date"
		case "customer":
			sortColumn = "customers.name"
			if !containsJoin(db, "customers") {
				db = db.Joins("LEFT JOIN customers ON customers.id = marketings.customer_id")
			}
		case "warehouse":
			sortColumn = "warehouses.name"
			if !containsJoin(db, "warehouses") {
				db = db.Joins("LEFT JOIN product_warehouses ON product_warehouses.id = marketing_products.product_warehouse_id").
					Joins("LEFT JOIN warehouses ON warehouses.id = product_warehouses.warehouse_id")
			}
		case "product":
			sortColumn = "products.name"
			if !containsJoin(db, "products") {
				db = db.Joins("LEFT JOIN product_warehouses ON product_warehouses.id = marketing_products.product_warehouse_id").
					Joins("LEFT JOIN products ON products.id = product_warehouses.product_id")
			}
		case "sales_person":
			sortColumn = "sales_users.name"
			if !containsJoin(db, "sales_users") {
				db = db.Joins("LEFT JOIN users AS sales_users ON sales_users.id = marketings.sales_person_id")
			}
		case "vehicle_number":
			sortColumn = "marketing_delivery_products.vehicle_number"
		case "sales_amount":
			sortColumn = "marketing_delivery_products.total_price"
		case "hpp_amount":
			sortColumn = "marketing_delivery_products.total_price"
		case "qty":
			sortColumn = "marketing_delivery_products.qty"
		case "average_weight":
			sortColumn = "marketing_delivery_products.avg_weight"
		case "total_weight":
			sortColumn = "marketing_delivery_products.total_weight"
		case "sales_price":
			sortColumn = "marketing_delivery_products.unit_price"
		case "hpp_price":
			sortColumn = "marketing_delivery_products.unit_price"
		case "aging_days":
			sortColumn = "marketing_delivery_products.delivery_date"
		}
	}

	if filters.SortOrder != "" && (filters.SortOrder == "asc" || filters.SortOrder == "desc") {
		sortOrder = strings.ToUpper(filters.SortOrder)
	}

	db = db.Order(sortColumn + " " + sortOrder)

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

	if err := db.
		Offset(offset).
		Limit(limit).
		Find(&deliveryProducts).Error; err != nil {
		return nil, 0, err
	}

	return deliveryProducts, total, nil
}

func containsJoin(db *gorm.DB, tableName string) bool {
	statement := db.Statement
	joinSQL := statement.SQL.String()
	return strings.Contains(joinSQL, "JOIN "+tableName)
}

func (r *MarketingDeliveryProductRepositoryImpl) UpdateFifoFields(ctx context.Context, id uint, usageQty, pendingQty float64) error {
	return r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Where("id = ?", id).
		Updates(map[string]interface{}{
			"usage_qty":   usageQty,
			"pending_qty": pendingQty,
		}).Error
}

func (r *MarketingDeliveryProductRepositoryImpl) GetUsageQty(ctx context.Context, id uint) (float64, error) {
	var usageQty float64
	err := r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Where("id = ?", id).
		Select("usage_qty").
		Scan(&usageQty).Error
	return usageQty, err
}

func (r *MarketingDeliveryProductRepositoryImpl) ResetFifoFields(ctx context.Context, id uint) error {
	return r.DB().WithContext(ctx).
		Model(&entity.MarketingDeliveryProduct{}).
		Where("id = ?", id).
		Updates(map[string]interface{}{
			"usage_qty":   0,
			"pending_qty": 0,
		}).Error
}
