package repository

import (
	"context"
	"errors"
	"math"
	"sort"
	"strings"
	"time"

	"gitlab.com/mbugroup/lti-api.git/internal/common/exportprogress"
	"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/production/recordings/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
	"gorm.io/gorm"
)

type RecordingRepository interface {
	repository.BaseRepository[entity.Recording]

	WithRelations(db *gorm.DB) *gorm.DB
	WithRelationsList(db *gorm.DB) *gorm.DB
	ApplyListFilters(db *gorm.DB, params *validation.Query) *gorm.DB
	ApplyListCountFilters(db *gorm.DB, params *validation.Query) *gorm.DB
	ApplySearchFilters(db *gorm.DB, rawSearch string) *gorm.DB
	GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query, modifier func(*gorm.DB) *gorm.DB) ([]entity.Recording, int64, error)
	GetLatestByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (*entity.Recording, error)
	ListByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, projectFlockKandangId uint, from *time.Time) ([]entity.Recording, error)
	GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error)
	CreateRecording(tx *gorm.DB, recording *entity.Recording) error

	CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error
	CreateStock(tx *gorm.DB, stock *entity.RecordingStock) error
	DeleteStocks(tx *gorm.DB, recordingID uint) error
	DeleteStocksByIDs(tx *gorm.DB, ids []uint) error
	ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error)
	GetStockByID(tx *gorm.DB, stockID uint) (*entity.RecordingStock, error)
	UpdateStockUsage(tx *gorm.DB, stockID uint, usageQty, pendingQty float64) error
	UpdateDepletionPending(tx *gorm.DB, depletionID uint, pendingQty float64) error
	UpdateDepletionQuantities(tx *gorm.DB, depletionID uint, qty, usageQty, pendingQty float64) error

	CreateDepletions(tx *gorm.DB, depletions []entity.RecordingDepletion) error
	DeleteDepletions(tx *gorm.DB, recordingID uint) error
	ListDepletions(tx *gorm.DB, recordingID uint) ([]entity.RecordingDepletion, error)
	GetDepletionByID(tx *gorm.DB, depletionID uint) (*entity.RecordingDepletion, error)

	CreateEggs(tx *gorm.DB, eggs []entity.RecordingEgg) error
	DeleteEggs(tx *gorm.DB, recordingID uint) error
	ListEggs(tx *gorm.DB, recordingID uint) ([]entity.RecordingEgg, error)
	UpdateEggTotalQty(tx *gorm.DB, eggID uint, totalQty float64) error
	GetRecordingEggByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.RecordingEgg, error)

	ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error)

	SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error)
	GetCumulativeDepletionByProjectFlockKandangUntil(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error)
	GetCumulativeDepletionByRecordingIDs(tx *gorm.DB, recordingIDs []uint) (map[uint]float64, error)
	GetUniformityMeanBwByWeek(tx *gorm.DB, projectFlockKandangId uint, week int) (float64, bool, error)
	FindPreviousRecording(tx *gorm.DB, projectFlockKandangId uint, currentDay int) (*entity.Recording, error)
	GetPreviousTotalChickByRecordingIDs(tx *gorm.DB, recordingIDs []uint) (map[uint]*float64, error)
	GetTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error)
	GetRemainingPopulationByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error)
	GetTotalChickByProjectFlockKandangIDs(tx *gorm.DB, projectFlockKandangIds []uint) (map[uint]int64, error)
	GetTotalChickinByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error)
	GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error)
	GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error)
	GetCumulativeEggQtyByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error)
	GetTotalWeightProducedFromUniformityByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
	GetTotalWeightProducedFromUniformityByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
	GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error)
	GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (totalDepletion float64, err error)
	GetTotalDepletionByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (totalDepletion float64, err error)
	GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (avgWeight float64, err error)
	GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeightKg float64, err error)
	GetAverageTargetMetricsByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint, includeTargets bool) (RecordingTargetAverages, error)
	GetProjectFlockKandangIDsByPopulationWarehouseIDs(ctx context.Context, tx *gorm.DB, productWarehouseIDs []uint) ([]uint, error)
	ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error
	ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error)
	GetProgressRows(ctx context.Context, startDate, endDate time.Time, allowedLocationIDs []uint, restrict bool) ([]exportprogress.Row, error)
}

type RecordingRepositoryImpl struct {
	*repository.BaseRepositoryImpl[entity.Recording]
}

type RecordingTargetAverages struct {
	HenDayAvg             float64
	HenDayCount           int64
	HenHouseAvg           float64
	HenHouseCount         int64
	EggWeightAvg          float64
	EggWeightCount        int64
	EggMassAvg            float64
	EggMassCount          int64
	FeedIntakeAvg         float64
	FeedIntakeCount       int64
	FcrAvg                float64
	FcrCount              int64
	CumDepletionRateAvg   float64
	CumDepletionRateCount int64
}

func NewRecordingRepository(db *gorm.DB) RecordingRepository {
	return &RecordingRepositoryImpl{
		BaseRepositoryImpl: repository.NewBaseRepository[entity.Recording](db),
	}
}

func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB {
	return db.
		Preload("CreatedUser").
		Preload("ProjectFlockKandang").
		Preload("ProjectFlockKandang.Kandang").
		Preload("ProjectFlockKandang.Kandang.Location").
		Preload("ProjectFlockKandang.ProjectFlock").
		Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard").
		// Preload("ProjectFlockKandang.ProjectFlock.Fcr").
		Preload("Depletions").
		Preload("Depletions.ProductWarehouse").
		Preload("Depletions.ProductWarehouse.Product").
		Preload("Depletions.ProductWarehouse.Product.Uom").
		Preload("Depletions.ProductWarehouse.Warehouse").
		Preload("Depletions.ProductWarehouse.Warehouse.Area").
		Preload("Depletions.ProductWarehouse.Warehouse.Location").
		Preload("Stocks").
		Preload("Stocks.ProductWarehouse").
		Preload("Stocks.ProductWarehouse.Product").
		Preload("Stocks.ProductWarehouse.Product.Uom").
		Preload("Stocks.ProductWarehouse.Warehouse").
		Preload("Stocks.ProductWarehouse.Warehouse.Area").
		Preload("Stocks.ProductWarehouse.Warehouse.Location").
		Preload("Eggs").
		Preload("Eggs.ProductWarehouse").
		Preload("Eggs.ProductWarehouse.Product").
		Preload("Eggs.ProductWarehouse.Product.Uom").
		Preload("Eggs.ProductWarehouse.Warehouse").
		Preload("Eggs.ProductWarehouse.Warehouse.Area").
		Preload("Eggs.ProductWarehouse.Warehouse.Location")
}

func (r *RecordingRepositoryImpl) WithRelationsList(db *gorm.DB) *gorm.DB {
	return db.
		Preload("CreatedUser").
		Preload("ProjectFlockKandang").
		Preload("ProjectFlockKandang.Kandang").
		Preload("ProjectFlockKandang.Kandang.Location").
		Preload("ProjectFlockKandang.ProjectFlock").
		Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard")
}

func (r *RecordingRepositoryImpl) latestApprovalSubQuery(db *gorm.DB) *gorm.DB {
	return db.Session(&gorm.Session{NewDB: true}).
		Table("approvals").
		Select("DISTINCT ON (approvable_id) approvable_id, action, step_name, notes").
		Where("approvable_type = ?", utils.ApprovalWorkflowRecording.String()).
		Order("approvable_id, action_at DESC, id DESC")
}

func (r *RecordingRepositoryImpl) applyStructuredListFilters(db *gorm.DB, params *validation.Query) *gorm.DB {
	if params == nil {
		return db
	}

	if params.ProjectFlockKandangId != 0 {
		db = db.Where("recordings.project_flock_kandangs_id = ?", params.ProjectFlockKandangId)
	}
	if params.ProjectFlockId != 0 {
		db = db.Where("pfk.project_flock_id = ?", params.ProjectFlockId)
	}
	if params.KandangId != 0 {
		db = db.Where("pfk.kandang_id = ?", params.KandangId)
	}
	if params.LocationId != 0 {
		db = db.Where("pf.location_id = ?", params.LocationId)
	}
	if params.AreaId != 0 {
		db = db.Where("pf.area_id = ?", params.AreaId)
	}
	if params.ProjectFlockCategory != "" {
		db = db.Where("UPPER(COALESCE(pf.category, '')) = ?", strings.ToUpper(strings.TrimSpace(params.ProjectFlockCategory)))
	}
	if params.ApprovalStatus != "" {
		db = db.Where(
			`EXISTS (
				SELECT 1
				FROM (?) AS latest_approval
				WHERE latest_approval.approvable_id = recordings.id
					AND UPPER(COALESCE(CAST(latest_approval.action AS TEXT), '')) = ?
			)`,
			r.latestApprovalSubQuery(db),
			strings.ToUpper(strings.TrimSpace(params.ApprovalStatus)),
		)
	}

	return db
}

func (r *RecordingRepositoryImpl) ApplyListFilters(db *gorm.DB, params *validation.Query) *gorm.DB {
	search := ""
	if params != nil {
		search = params.Search
	}
	db = r.WithRelationsList(db)
	db = db.
		Joins("JOIN project_flock_kandangs pfk ON pfk.id = recordings.project_flock_kandangs_id").
		Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
		Joins("LEFT JOIN kandangs k ON k.id = pfk.kandang_id")
	db = r.applyStructuredListFilters(db, params)
	db = r.ApplySearchFilters(db, search)
	return db.Order("recordings.record_datetime DESC").Order("recordings.created_at DESC")
}

func (r *RecordingRepositoryImpl) ApplyListCountFilters(db *gorm.DB, params *validation.Query) *gorm.DB {
	search := ""
	if params != nil {
		search = params.Search
	}
	db = db.
		Joins("JOIN project_flock_kandangs pfk ON pfk.id = recordings.project_flock_kandangs_id").
		Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
		Joins("LEFT JOIN kandangs k ON k.id = pfk.kandang_id")
	db = r.applyStructuredListFilters(db, params)
	db = r.ApplySearchFilters(db, search)
	return db
}

func (r *RecordingRepositoryImpl) GetAllWithFilters(ctx context.Context, offset, limit int, params *validation.Query, modifier func(*gorm.DB) *gorm.DB) ([]entity.Recording, int64, error) {
	var (
		records []entity.Recording
		total   int64
	)

	countQ := r.ApplyListCountFilters(r.DB().WithContext(ctx).Model(&entity.Recording{}), params)
	if modifier != nil {
		countQ = modifier(countQ)
	}
	if err := countQ.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	listQ := r.ApplyListFilters(r.DB().WithContext(ctx).Model(&entity.Recording{}), params)
	if modifier != nil {
		listQ = modifier(listQ)
	}
	if err := listQ.Offset(offset).Limit(limit).Find(&records).Error; err != nil {
		return nil, 0, err
	}

	return records, total, nil
}

func (r *RecordingRepositoryImpl) ApplySearchFilters(db *gorm.DB, rawSearch string) *gorm.DB {
	normalized := strings.ToLower(strings.TrimSpace(rawSearch))
	if normalized == "" {
		return db
	}

	likeQuery := "%" + normalized + "%"
	subQuery := db.Session(&gorm.Session{NewDB: true}).
		Table("recordings").
		Select("recordings.id").
		Joins("LEFT JOIN project_flock_kandangs pfk ON pfk.id = recordings.project_flock_kandangs_id").
		Joins("LEFT JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
		Joins("LEFT JOIN kandangs k ON k.id = pfk.kandang_id").
		Joins("LEFT JOIN locations l ON l.id = k.location_id").
		Joins("LEFT JOIN recording_stocks rs ON rs.recording_id = recordings.id").
		Joins("LEFT JOIN recording_depletions rd ON rd.recording_id = recordings.id").
		Joins("LEFT JOIN recording_eggs re ON re.recording_id = recordings.id").
		Joins("LEFT JOIN product_warehouses pws ON pws.id = rs.product_warehouse_id").
		Joins("LEFT JOIN product_warehouses pwd ON pwd.id = rd.product_warehouse_id").
		Joins("LEFT JOIN product_warehouses pwe ON pwe.id = re.product_warehouse_id").
		Joins("LEFT JOIN warehouses ws ON ws.id = pws.warehouse_id").
		Joins("LEFT JOIN warehouses wd ON wd.id = pwd.warehouse_id").
		Joins("LEFT JOIN warehouses we ON we.id = pwe.warehouse_id").
		Joins("LEFT JOIN users cu ON cu.id = recordings.created_by").
		Joins("LEFT JOIN (?) AS latest_approval ON latest_approval.approvable_id = recordings.id", r.latestApprovalSubQuery(db)).
		Where(`
			LOWER(pf.flock_name) LIKE ?
			OR LOWER(k.name) LIKE ?
			OR LOWER(l.name) LIKE ?
			OR LOWER(l.address) LIKE ?
			OR LOWER(ws.name) LIKE ?
			OR LOWER(wd.name) LIKE ?
			OR LOWER(we.name) LIKE ?
			OR LOWER(COALESCE(cu.name, '')) LIKE ?
			OR LOWER(COALESCE(latest_approval.step_name, '')) LIKE ?
			OR LOWER(COALESCE(CAST(latest_approval.action AS TEXT), '')) LIKE ?
			OR LOWER(COALESCE(latest_approval.notes, '')) LIKE ?`,
			likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery, likeQuery,
		)
	return db.Where("recordings.id IN (?)", subQuery)
}

func (r *RecordingRepositoryImpl) GetLatestByProjectFlockKandangID(ctx context.Context, projectFlockKandangId uint) (*entity.Recording, error) {
	if projectFlockKandangId == 0 {
		return nil, errors.New("project_flock_kandang_id is required")
	}

	var record entity.Recording
	err := r.DB().WithContext(ctx).
		Where("project_flock_kandangs_id = ?", projectFlockKandangId).
		Order("record_datetime DESC").
		Order("created_at DESC").
		Limit(1).
		Find(&record).Error
	if errors.Is(err, gorm.ErrRecordNotFound) || record.Id == 0 {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &record, nil
}

func (r *RecordingRepositoryImpl) GetProgressRows(ctx context.Context, startDate, endDate time.Time, allowedLocationIDs []uint, restrict bool) ([]exportprogress.Row, error) {
	const unassignedSQL = "'" + exportprogress.UnassignedKandangName + "'"
	query := r.DB().WithContext(ctx).
		Table("recordings AS r").
		Select(`
			'Recordings' AS module,
			COALESCE(pf.flock_name, loc.name, 'Unknown Farm') AS farm_name,
			COALESCE(k.name, `+unassignedSQL+`, 'Unknown Kandang') AS kandang_name,
			CAST(DATE(r.record_datetime) AS TEXT) AS activity_date,
			COUNT(*) AS count
		`).
		Joins("JOIN project_flock_kandangs pfk ON pfk.id = r.project_flock_kandangs_id").
		Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
		Joins("JOIN kandangs k ON k.id = pfk.kandang_id").
		Joins("LEFT JOIN locations loc ON loc.id = k.location_id").
		Where("r.deleted_at IS NULL").
		Where("DATE(r.record_datetime) >= DATE(?)", startDate).
		Where("DATE(r.record_datetime) <= DATE(?)", endDate)

	if restrict {
		if len(allowedLocationIDs) == 0 {
			return []exportprogress.Row{}, nil
		}
		query = query.Where("pf.location_id IN ?", allowedLocationIDs)
	}

	type progressRowResult struct {
		Module       string
		FarmName     string
		KandangName  string
		ActivityDate string
		Count        int
	}
	scanned := make([]progressRowResult, 0)
	err := query.
		Group("DATE(r.record_datetime), COALESCE(pf.flock_name, loc.name, 'Unknown Farm'), COALESCE(k.name, " + unassignedSQL + ", 'Unknown Kandang')").
		Order("activity_date ASC, farm_name ASC, kandang_name ASC").
		Scan(&scanned).Error
	if err != nil {
		return nil, err
	}

	rows := make([]exportprogress.Row, 0, len(scanned))
	for _, item := range scanned {
		activityDate, err := exportprogress.ParseActivityDate(item.ActivityDate)
		if err != nil {
			return nil, err
		}
		rows = append(rows, exportprogress.Row{
			Module:       item.Module,
			FarmName:     item.FarmName,
			KandangName:  item.KandangName,
			ActivityDate: activityDate,
			Count:        item.Count,
		})
	}
	return rows, nil
}

func (r *RecordingRepositoryImpl) ListByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, projectFlockKandangId uint, from *time.Time) ([]entity.Recording, error) {
	if projectFlockKandangId == 0 {
		return nil, errors.New("project_flock_kandang_id is required")
	}

	db := tx.WithContext(ctx).
		Model(&entity.Recording{}).
		Where("project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("deleted_at IS NULL")

	if from != nil {
		db = db.Where("record_datetime >= ?", *from)
	}

	var records []entity.Recording
	if err := db.Order("record_datetime ASC").Order("created_at ASC").Find(&records).Error; err != nil {
		return nil, err
	}
	return records, nil
}

func (r *RecordingRepositoryImpl) GenerateNextDay(tx *gorm.DB, projectFlockKandangId uint) (int, error) {
	var days []int
	if err := tx.Model(&entity.Recording{}).
		Where("project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("deleted_at IS NULL").
		Where("day IS NOT NULL").
		Pluck("day", &days).Error; err != nil {
		return 0, err
	}
	return nextRecordingDay(days), nil
}

func (r *RecordingRepositoryImpl) CreateRecording(tx *gorm.DB, recording *entity.Recording) error {
	if recording == nil {
		return nil
	}
	return tx.Select(
		"ProjectFlockKandangId",
		"RecordDatetime",
		"Day",
		"CreatedBy",
	).Create(recording).Error
}

func (r *RecordingRepositoryImpl) CreateStocks(tx *gorm.DB, stocks []entity.RecordingStock) error {
	if len(stocks) == 0 {
		return nil
	}
	return tx.Create(&stocks).Error
}

func (r *RecordingRepositoryImpl) CreateStock(tx *gorm.DB, stock *entity.RecordingStock) error {
	if stock == nil {
		return nil
	}
	return tx.Create(stock).Error
}

func (r *RecordingRepositoryImpl) DeleteStocks(tx *gorm.DB, recordingID uint) error {
	return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingStock{}).Error
}

func (r *RecordingRepositoryImpl) DeleteStocksByIDs(tx *gorm.DB, ids []uint) error {
	if len(ids) == 0 {
		return nil
	}
	return tx.Where("id IN ?", ids).Delete(&entity.RecordingStock{}).Error
}

func (r *RecordingRepositoryImpl) ListStocks(tx *gorm.DB, recordingID uint) ([]entity.RecordingStock, error) {
	var items []entity.RecordingStock
	if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
		return nil, err
	}
	return items, nil
}

func (r *RecordingRepositoryImpl) GetStockByID(tx *gorm.DB, stockID uint) (*entity.RecordingStock, error) {
	if stockID == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	var stock entity.RecordingStock
	if err := tx.Where("id = ?", stockID).Take(&stock).Error; err != nil {
		return nil, err
	}
	return &stock, nil
}

func (r *RecordingRepositoryImpl) UpdateStockUsage(tx *gorm.DB, stockID uint, usageQty, pendingQty float64) error {
	return tx.Model(&entity.RecordingStock{}).
		Where("id = ?", stockID).
		Updates(map[string]any{
			"usage_qty":   usageQty,
			"pending_qty": pendingQty,
		}).Error
}

func (r *RecordingRepositoryImpl) UpdateDepletionPending(tx *gorm.DB, depletionID uint, pendingQty float64) error {
	return tx.Model(&entity.RecordingDepletion{}).
		Where("id = ?", depletionID).
		Update("pending_qty", pendingQty).Error
}

func (r *RecordingRepositoryImpl) UpdateDepletionQuantities(tx *gorm.DB, depletionID uint, qty, usageQty, pendingQty float64) error {
	return tx.Model(&entity.RecordingDepletion{}).
		Where("id = ?", depletionID).
		Updates(map[string]any{
			"qty":         qty,
			"usage_qty":   usageQty,
			"pending_qty": pendingQty,
		}).Error
}

func (r *RecordingRepositoryImpl) CreateDepletions(tx *gorm.DB, depletions []entity.RecordingDepletion) error {
	if len(depletions) == 0 {
		return nil
	}
	return tx.Create(&depletions).Error
}

func (r *RecordingRepositoryImpl) DeleteDepletions(tx *gorm.DB, recordingID uint) error {
	return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingDepletion{}).Error
}

func (r *RecordingRepositoryImpl) ListDepletions(tx *gorm.DB, recordingID uint) ([]entity.RecordingDepletion, error) {
	var items []entity.RecordingDepletion
	if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
		return nil, err
	}
	return items, nil
}

func (r *RecordingRepositoryImpl) GetDepletionByID(tx *gorm.DB, depletionID uint) (*entity.RecordingDepletion, error) {
	if depletionID == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	var depletion entity.RecordingDepletion
	if err := tx.Where("id = ?", depletionID).Take(&depletion).Error; err != nil {
		return nil, err
	}
	return &depletion, nil
}

func (r *RecordingRepositoryImpl) CreateEggs(tx *gorm.DB, eggs []entity.RecordingEgg) error {
	if len(eggs) == 0 {
		return nil
	}
	return tx.Create(&eggs).Error
}

func (r *RecordingRepositoryImpl) DeleteEggs(tx *gorm.DB, recordingID uint) error {
	return tx.Where("recording_id = ?", recordingID).Delete(&entity.RecordingEgg{}).Error
}

func (r *RecordingRepositoryImpl) ListEggs(tx *gorm.DB, recordingID uint) ([]entity.RecordingEgg, error) {
	var items []entity.RecordingEgg
	if err := tx.Where("recording_id = ?", recordingID).Find(&items).Error; err != nil {
		return nil, err
	}
	return items, nil
}

func (r *RecordingRepositoryImpl) UpdateEggTotalQty(tx *gorm.DB, eggID uint, totalQty float64) error {
	return tx.Model(&entity.RecordingEgg{}).
		Where("id = ?", eggID).
		Update("total_qty", totalQty).Error
}

func (r *RecordingRepositoryImpl) GetRecordingEggByID(
	ctx context.Context,
	id uint,
	modifier func(*gorm.DB) *gorm.DB,
) (*entity.RecordingEgg, error) {
	if id == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	db := r.DB()
	if modifier != nil {
		db = modifier(db)
	}

	var egg entity.RecordingEgg
	query := db.WithContext(ctx).
		Preload("Recording").
		Preload("Recording.ProjectFlockKandang").
		Preload("Recording.ProjectFlockKandang.ProjectFlock").
		Preload("ProductWarehouse").
		Where("id = ?", id)

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

func (r *RecordingRepositoryImpl) ExistsOnDate(ctx context.Context, projectFlockKandangId uint, recordTime time.Time) (bool, error) {
	if projectFlockKandangId == 0 {
		return false, nil
	}

	ref := recordTime.In(time.UTC)
	startOfDay := time.Date(ref.Year(), ref.Month(), ref.Day(), 0, 0, 0, 0, time.UTC)
	endOfDay := startOfDay.Add(24 * time.Hour)

	var count int64
	err := r.DB().
		WithContext(ctx).
		Model(&entity.Recording{}).
		Where("project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("record_datetime >= ? AND record_datetime < ?", startOfDay, endOfDay).
		Count(&count).Error
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (r *RecordingRepositoryImpl) SumRecordingDepletions(tx *gorm.DB, recordingID uint) (float64, error) {
	var result float64
	if err := tx.Model(&entity.RecordingDepletion{}).
		Where("recording_id = ?", recordingID).
		Select("COALESCE(SUM(qty), 0)").
		Scan(&result).Error; err != nil {
		return 0, err
	}
	return result, nil
}

func (r *RecordingRepositoryImpl) GetCumulativeDepletionByProjectFlockKandangUntil(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error) {
	if projectFlockKandangId == 0 || recordTime.IsZero() {
		return 0, nil
	}

	var total float64
	err := tx.
		Table("recording_depletions rd").
		Select("COALESCE(SUM(rd.qty),0)").
		Joins("JOIN recordings r ON r.id = rd.recording_id").
		Where("r.project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("r.record_datetime <= ?", recordTime).
		Where("r.deleted_at IS NULL").
		Scan(&total).Error
	return total, err
}

func (r *RecordingRepositoryImpl) GetCumulativeDepletionByRecordingIDs(tx *gorm.DB, recordingIDs []uint) (map[uint]float64, error) {
	result := make(map[uint]float64)
	if len(recordingIDs) == 0 {
		return result, nil
	}

	type row struct {
		RecordingID uint    `gorm:"column:recording_id"`
		Total       float64 `gorm:"column:total_qty"`
	}
	var rows []row

	err := tx.
		Table("recordings r").
		Select("r.id AS recording_id, COALESCE(SUM(rd.qty), 0) AS total_qty").
		Joins(`
			LEFT JOIN recordings r2
				ON r2.project_flock_kandangs_id = r.project_flock_kandangs_id
				AND r2.record_datetime <= r.record_datetime
				AND r2.deleted_at IS NULL`).
		Joins("LEFT JOIN recording_depletions rd ON rd.recording_id = r2.id").
		Where("r.id IN ?", recordingIDs).
		Where("r.deleted_at IS NULL").
		Group("r.id").
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}

	for _, row := range rows {
		result[row.RecordingID] = row.Total
	}
	return result, nil
}

func (r *RecordingRepositoryImpl) GetUniformityMeanBwByWeek(tx *gorm.DB, projectFlockKandangId uint, week int) (float64, bool, error) {
	if projectFlockKandangId == 0 || week <= 0 {
		return 0, false, nil
	}

	var row struct {
		ID     uint
		MeanUp float64
	}
	if err := tx.
		Table("project_flock_kandang_uniformity").
		Select("id, mean_up").
		Where("project_flock_kandang_id = ?", projectFlockKandangId).
		Where("week = ?", week).
		Order("id DESC").
		Limit(1).
		Scan(&row).Error; err != nil {
		return 0, false, err
	}
	if row.ID == 0 {
		return 0, false, nil
	}

	meanBw := row.MeanUp / 1.10
	return meanBw, true, nil
}

func (r *RecordingRepositoryImpl) FindPreviousRecording(tx *gorm.DB, projectFlockKandangId uint, currentDay int) (*entity.Recording, error) {
	if currentDay <= 1 {
		return nil, nil
	}

	var prev entity.Recording
	err := tx.
		Where("project_flock_kandangs_id = ? AND day < ?", projectFlockKandangId, currentDay).
		Where("day IS NOT NULL").
		Order("day DESC").
		Limit(1).
		Find(&prev).Error

	if errors.Is(err, gorm.ErrRecordNotFound) || prev.Id == 0 {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &prev, nil
}

func (r *RecordingRepositoryImpl) GetPreviousTotalChickByRecordingIDs(tx *gorm.DB, recordingIDs []uint) (map[uint]*float64, error) {
	result := make(map[uint]*float64)
	if len(recordingIDs) == 0 {
		return result, nil
	}

	type row struct {
		RecordingID       uint     `gorm:"column:recording_id"`
		PrevTotalChickQty *float64 `gorm:"column:prev_total_chick_qty"`
	}
	var rows []row

	err := tx.
		Table("recordings r").
		Select(`
			r.id AS recording_id,
			(
				SELECT r2.total_chick_qty
				FROM recordings r2
				WHERE r2.project_flock_kandangs_id = r.project_flock_kandangs_id
				  AND r2.day IS NOT NULL
				  AND r.day IS NOT NULL
				  AND r2.day < r.day
				  AND r2.deleted_at IS NULL
				ORDER BY r2.day DESC
				LIMIT 1
			) AS prev_total_chick_qty`).
		Where("r.id IN ?", recordingIDs).
		Where("r.deleted_at IS NULL").
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}

	for _, row := range rows {
		result[row.RecordingID] = row.PrevTotalChickQty
	}
	return result, nil
}

func (r *RecordingRepositoryImpl) GetTotalChick(tx *gorm.DB, projectFlockKandangId uint) (int64, error) {
	var total float64
	err := tx.
		Table("project_flock_populations").
		Select("COALESCE(SUM(project_flock_populations.total_qty - project_flock_populations.total_used_qty), 0) AS total_qty").
		Joins("JOIN project_chickins ON project_chickins.id = project_flock_populations.project_chickin_id").
		Where("project_chickins.project_flock_kandang_id = ?", projectFlockKandangId).
		Scan(&total).Error
	if err != nil {
		return 0, err
	}

	if total < 0 {
		total = 0
	}

	return int64(math.Round(total)), nil
}

func (r *RecordingRepositoryImpl) GetRemainingPopulationByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error) {
	var total float64
	err := tx.
		Table("project_flock_populations").
		Select("COALESCE(SUM(project_flock_populations.total_qty - project_flock_populations.total_used_qty), 0) AS total_qty").
		Joins("JOIN project_chickins ON project_chickins.id = project_flock_populations.project_chickin_id").
		Where("project_chickins.project_flock_kandang_id = ?", projectFlockKandangId).
		Scan(&total).Error
	if err != nil {
		return 0, err
	}
	if total < 0 {
		total = 0
	}
	return total, nil
}

func (r *RecordingRepositoryImpl) GetTotalChickByProjectFlockKandangIDs(tx *gorm.DB, projectFlockKandangIds []uint) (map[uint]int64, error) {
	result := make(map[uint]int64)
	if len(projectFlockKandangIds) == 0 {
		return result, nil
	}

	type row struct {
		ProjectFlockKandangId uint    `gorm:"column:project_flock_kandang_id"`
		Total                 float64 `gorm:"column:total_qty"`
	}
	var rows []row

	err := tx.
		Table("project_flock_populations pfp").
		Select("project_chickins.project_flock_kandang_id, COALESCE(SUM(pfp.total_qty - pfp.total_used_qty), 0) AS total_qty").
		Joins("JOIN project_chickins ON project_chickins.id = pfp.project_chickin_id").
		Where("project_chickins.project_flock_kandang_id IN ?", projectFlockKandangIds).
		Group("project_chickins.project_flock_kandang_id").
		Scan(&rows).Error
	if err != nil {
		return nil, err
	}

	for _, row := range rows {
		total := math.Round(row.Total)
		if total < 0 {
			total = 0
		}
		result[row.ProjectFlockKandangId] = int64(total)
	}

	return result, nil
}

func (r *RecordingRepositoryImpl) GetTotalChickinByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint) (float64, error) {
	if projectFlockKandangId == 0 {
		return 0, nil
	}

	var result float64
	err := tx.
		Table("project_chickins").
		Select("COALESCE(SUM(project_chickins.usage_qty), 0)").
		Where("project_chickins.project_flock_kandang_id = ?", projectFlockKandangId).
		Scan(&result).Error
	return result, err
}

func (r *RecordingRepositoryImpl) GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error) {
	var result struct {
		TotalQty float64
	}

	if err := tx.
		Table("recording_stocks").
		Select("COALESCE(SUM(COALESCE(recording_stocks.usage_qty, 0) + COALESCE(recording_stocks.pending_qty, 0)), 0) AS total_qty").
		Joins("JOIN product_warehouses ON product_warehouses.id = recording_stocks.product_warehouse_id").
		Joins("JOIN products ON products.id = product_warehouses.product_id").
		Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ? AND UPPER(flags.name) = ?", entity.FlagableTypeProduct, "PAKAN").
		Where("recording_stocks.recording_id = ?", recordingID).
		Scan(&result).Error; err != nil {
		return 0, err
	}

	if result.TotalQty <= 0 {
		return 0, nil
	}
	return result.TotalQty * 1000, nil
}

func (r *RecordingRepositoryImpl) GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error) {
	if recordingID == 0 {
		return 0, 0, nil
	}

	var result struct {
		TotalQty         float64
		TotalWeightGrams float64
	}
	err = tx.
		Table("recording_eggs").
		Select("COALESCE(SUM(recording_eggs.qty), 0) AS total_qty, COALESCE(SUM(COALESCE(recording_eggs.weight, 0) * 1000), 0) AS total_weight_grams").
		Where("recording_eggs.recording_id = ?", recordingID).
		Scan(&result).Error
	if err != nil {
		return 0, 0, err
	}
	return result.TotalQty, result.TotalWeightGrams, nil
}

func (r *RecordingRepositoryImpl) GetCumulativeEggQtyByProjectFlockKandang(
	tx *gorm.DB,
	projectFlockKandangId uint,
	recordTime time.Time,
) (float64, error) {
	if projectFlockKandangId == 0 {
		return 0, nil
	}

	var cumulativeEggQty float64
	err := tx.
		Table("recording_eggs").
		Select("COALESCE(SUM(recording_eggs.qty), 0)").
		Joins("JOIN recordings ON recordings.id = recording_eggs.recording_id").
		Where("recordings.project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("recordings.record_datetime <= ?", recordTime).
		Scan(&cumulativeEggQty).Error
	if err != nil {
		return 0, err
	}

	productWarehouseSubQuery := tx.
		Table("recording_eggs").
		Select("DISTINCT recording_eggs.product_warehouse_id").
		Joins("JOIN recordings ON recordings.id = recording_eggs.recording_id").
		Where("recordings.project_flock_kandangs_id = ?", projectFlockKandangId).
		Where("recordings.record_datetime <= ?", recordTime)

	var adjustmentEggQty float64
	err = tx.
		Table("adjustment_stocks").
		Select("COALESCE(SUM(adjustment_stocks.total_qty), 0)").
		Where("adjustment_stocks.product_warehouse_id IN (?)", productWarehouseSubQuery).
		Where("adjustment_stocks.function_code = ?", "RECORDING_EGG_IN").
		Where("adjustment_stocks.transaction_type = ?", "RECORDING").
		Where("adjustment_stocks.created_at <= ?", recordTime).
		Scan(&adjustmentEggQty).Error
	if err != nil {
		return 0, err
	}

	return cumulativeEggQty + adjustmentEggQty, nil
}
func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) {
	// Body-weight tracking is removed; keep stub for report compatibility.
	return 0, 0, nil
}

func (r *RecordingRepositoryImpl) GetTotalDepletionByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
	var result float64
	err := r.DB().WithContext(ctx).
		Table("recording_depletions").
		Select("COALESCE(SUM(recording_depletions.qty), 0)").
		Joins("JOIN recordings ON recordings.id = recording_depletions.recording_id").
		Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = recordings.project_flock_kandangs_id").
		Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
		Scan(&result).Error
	return result, err
}

func (r *RecordingRepositoryImpl) GetTotalDepletionByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) {
	var result float64
	err := r.DB().WithContext(ctx).
		Table("recording_depletions").
		Select("COALESCE(SUM(recording_depletions.qty), 0)").
		Joins("JOIN recordings ON recordings.id = recording_depletions.recording_id").
		Where("recordings.project_flock_kandangs_id = ?", projectFlockKandangID).
		Scan(&result).Error
	return result, err
}

func (r *RecordingRepositoryImpl) GetLatestAvgWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
	// Body-weight tracking is removed; keep stub for report compatibility.
	return 0, nil
}

func (r *RecordingRepositoryImpl) GetTotalEggProductionWeightByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
	if projectFlockID == 0 {
		return 0, nil
	}

	var result float64
	err := r.DB().WithContext(ctx).
		Table("recording_eggs").
		Select("COALESCE(SUM(recording_eggs.weight), 0)").
		Joins("JOIN recordings ON recordings.id = recording_eggs.recording_id").
		Joins("JOIN project_flock_kandangs ON project_flock_kandangs.id = recordings.project_flock_kandangs_id").
		Where("project_flock_kandangs.project_flock_id = ?", projectFlockID).
		Scan(&result).Error
	return result, err
}

func (r *RecordingRepositoryImpl) GetAverageTargetMetricsByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint, includeTargets bool) (RecordingTargetAverages, error) {
	var row struct {
		HenDayTotal           float64
		HenHouseTotal         float64
		EggWeightTotal        float64
		EggMassTotal          float64
		FeedIntakeTotal       float64
		FcrTotal              float64
		CumDepletionRateTotal float64
		TotalCount            int64
	}

	selectParts := []string{
		"COALESCE(SUM(feed_intake), 0) AS feed_intake_total",
		"COALESCE(SUM(fcr_value), 0) AS fcr_total",
		"COALESCE(SUM(cum_depletion_rate), 0) AS cum_depletion_rate_total",
		"COUNT(*) AS total_count",
	}
	if includeTargets {
		selectParts = append([]string{
			"COALESCE(SUM(hen_day), 0) AS hen_day_total",
			"COALESCE(SUM(hen_house), 0) AS hen_house_total",
			"COALESCE(SUM(egg_weight), 0) AS egg_weight_total",
			"COALESCE(SUM(egg_mass), 0) AS egg_mass_total",
		}, selectParts...)
	}

	if err := r.DB().WithContext(ctx).
		Table("recordings").
		Select(strings.Join(selectParts, ", ")).
		Where("project_flock_kandangs_id = ? AND deleted_at IS NULL", projectFlockKandangID).
		Scan(&row).Error; err != nil {
		return RecordingTargetAverages{}, err
	}

	result := RecordingTargetAverages{
		FeedIntakeCount:       row.TotalCount,
		FcrCount:              row.TotalCount,
		CumDepletionRateCount: row.TotalCount,
	}
	if includeTargets {
		result.HenDayCount = row.TotalCount
		result.HenHouseCount = row.TotalCount
		result.EggWeightCount = row.TotalCount
		result.EggMassCount = row.TotalCount
	}
	if row.TotalCount > 0 {
		if includeTargets {
			result.HenDayAvg = row.HenDayTotal / float64(row.TotalCount)
			result.HenHouseAvg = row.HenHouseTotal / float64(row.TotalCount)
			result.EggWeightAvg = row.EggWeightTotal / float64(row.TotalCount)
			result.EggMassAvg = row.EggMassTotal / float64(row.TotalCount)
		}
		result.FeedIntakeAvg = row.FeedIntakeTotal / float64(row.TotalCount)
		result.FcrAvg = row.FcrTotal / float64(row.TotalCount)
		result.CumDepletionRateAvg = row.CumDepletionRateTotal
	}

	return result, nil
}

func (r *RecordingRepositoryImpl) GetProjectFlockKandangIDsByPopulationWarehouseIDs(
	ctx context.Context,
	tx *gorm.DB,
	productWarehouseIDs []uint,
) ([]uint, error) {
	if len(productWarehouseIDs) == 0 {
		return nil, nil
	}

	db := r.DB().WithContext(ctx)
	if tx != nil {
		db = tx.WithContext(ctx)
	}

	var kandangIDs []uint
	if err := db.Table("project_flock_populations pfp").
		Select("DISTINCT pc.project_flock_kandang_id").
		Joins("JOIN project_chickins pc ON pc.id = pfp.project_chickin_id").
		Where("pfp.product_warehouse_id IN ?", productWarehouseIDs).
		Where("pfp.deleted_at IS NULL").
		Where("pc.deleted_at IS NULL").
		Pluck("pc.project_flock_kandang_id", &kandangIDs).Error; err != nil {
		return nil, err
	}

	return kandangIDs, nil
}

func (r *RecordingRepositoryImpl) ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error {
	if projectFlockKandangID == 0 {
		return nil
	}

	idsSubquery := `
		SELECT pfp.id
		FROM project_flock_populations pfp
		JOIN project_chickins pc ON pc.id = pfp.project_chickin_id
		WHERE pc.project_flock_kandang_id = ?
	`

	updateWithAlloc := `
		UPDATE project_flock_populations p
		SET total_used_qty = COALESCE(a.used, 0)
		FROM (
			SELECT stockable_id, SUM(qty) AS used
			FROM stock_allocations
			WHERE stockable_type = 'PROJECT_FLOCK_POPULATION'
			  AND status = 'ACTIVE'
			  AND allocation_purpose = 'CONSUME'
			GROUP BY stockable_id
		) a
		WHERE p.id = a.stockable_id
		  AND p.id IN (` + idsSubquery + `)
	`

	resetMissing := `
		UPDATE project_flock_populations p
		SET total_used_qty = 0
		WHERE p.id IN (` + idsSubquery + `)
			AND NOT EXISTS (
				SELECT 1
				FROM stock_allocations sa
				WHERE sa.stockable_type = 'PROJECT_FLOCK_POPULATION'
				  AND sa.status = 'ACTIVE'
				  AND sa.allocation_purpose = 'CONSUME'
				  AND sa.stockable_id = p.id
			  )
		`

	db := r.DB().WithContext(ctx)
	if tx != nil {
		db = tx.WithContext(ctx)
	}

	if err := db.Exec(updateWithAlloc, projectFlockKandangID).Error; err != nil {
		return err
	}
	if err := db.Exec(resetMissing, projectFlockKandangID).Error; err != nil {
		return err
	}
	return nil
}

func (r *RecordingRepositoryImpl) ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error) {
	if len(ids) == 0 {
		return 0, nil
	}
	var invalidIDs []uint
	if err := r.DB().WithContext(ctx).
		Table("product_warehouses pw").
		Where("pw.id IN ?", ids).
		Where(`NOT EXISTS (
			SELECT 1 FROM flags f
			WHERE f.flagable_type = 'products'
			  AND f.flagable_id = pw.product_id
			  AND UPPER(f.name) IN ?
		)`, flags).
		Pluck("pw.id", &invalidIDs).Error; err != nil {
		return 0, err
	}
	if len(invalidIDs) > 0 {
		return invalidIDs[0], nil
	}
	return 0, nil
}

func nextRecordingDay(days []int) int {
	if len(days) == 0 {
		return 1
	}

	unique := make(map[int]struct{}, len(days))
	for _, day := range days {
		if day > 0 {
			unique[day] = struct{}{}
		}
	}

	normalized := make([]int, 0, len(unique))
	for day := range unique {
		normalized = append(normalized, day)
	}
	sort.Ints(normalized)

	for idx, day := range normalized {
		expected := idx + 1
		if day != expected {
			return expected
		}
	}

	return len(normalized) + 1
}

// GetTotalWeightProducedFromUniformityByProjectFlockID calculates total weight produced from uniformity data
// It takes the latest uniformity record per kandang and calculates: SUM(mean_weight * chick_qty_of_weight / 1000)
func (r *RecordingRepositoryImpl) GetTotalWeightProducedFromUniformityByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error) {
	if projectFlockID == 0 {
		return 0, nil
	}

	var result struct {
		TotalWeight float64
	}

	err := r.DB().WithContext(ctx).
		Table("project_flock_kandang_uniformity").
		Select("COALESCE(SUM((mean_up / 1.10) * chick_qty_of_weight / 1000), 0) as total_weight").
		Joins("JOIN ("+
			"	SELECT pfku.project_flock_kandang_id, MAX(pfku.id) as latest_id "+
			"	FROM project_flock_kandang_uniformity pfku "+
			"	JOIN project_flock_kandangs pfk ON pfk.id = pfku.project_flock_kandang_id "+
			"	WHERE pfk.project_flock_id = ? "+
			"	GROUP BY pfku.project_flock_kandang_id "+
			") latest ON latest.project_flock_kandang_id = project_flock_kandang_uniformity.project_flock_kandang_id "+
			"AND project_flock_kandang_uniformity.id = latest.latest_id", projectFlockID).
		Scan(&result).Error

	return result.TotalWeight, err
}

func (r *RecordingRepositoryImpl) GetTotalWeightProducedFromUniformityByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error) {
	if projectFlockKandangID == 0 {
		return 0, nil
	}

	var result struct {
		TotalWeight float64
	}

	err := r.DB().WithContext(ctx).
		Table("project_flock_kandang_uniformity").
		Select("COALESCE((mean_up / 1.10) * chick_qty_of_weight / 1000, 0) as total_weight").
		Where("project_flock_kandang_id = ?", projectFlockKandangID).
		Order("id DESC").
		Limit(1).
		Scan(&result).Error

	return result.TotalWeight, err
}
