package repository

import (
	"context"
	"strings"
	"time"

	"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/project-flock-kandangs/validations"
	"gorm.io/gorm"
)

type ProjectFlockKandangRepository interface {
	GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error)
	GetByIDLight(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error)
	GetByProjectFlockAndKandang(ctx context.Context, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error)
	GetActiveByKandangID(ctx context.Context, kandangID uint) (*entity.ProjectFlockKandang, error)
	UpdateClosedAt(ctx context.Context, id uint, t *time.Time) error
	CreateMany(ctx context.Context, records []*entity.ProjectFlockKandang) error
	DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error
	GetAll(ctx context.Context, offset int, limit int, modifier func(*gorm.DB) *gorm.DB) ([]entity.ProjectFlockKandang, int64, error)
	GetAllWithFilters(ctx context.Context, offset int, limit int, params interface{}) ([]entity.ProjectFlockKandang, int64, error)
	GetAllWithFiltersScoped(ctx context.Context, offset int, limit int, params interface{}, locationIDs []uint, restrict bool) ([]entity.ProjectFlockKandang, int64, error)
	GetAllNameWithPeriodeScoped(ctx context.Context, offset int, limit int, params *validation.Query, locationIDs []uint, restrict bool) ([]ProjectFlockKandangNameWithPeriode, int64, error)
	GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectFlockKandang, error)
	ListExistingKandangIDs(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error)
	HasKandangsLinkedToOtherProject(ctx context.Context, kandangIDs []uint, exceptProjectID *uint) (bool, error)
	FindKandangsWithRecordings(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]entity.Kandang, error)
	MaxPeriodByBaseName(ctx context.Context, baseName string) (int, error)
	ProjectPeriodsByProjectIDs(ctx context.Context, projectIDs []uint) (map[uint]int, error)
	HasOpenNewerPeriod(ctx context.Context, kandangID uint, currentPeriod int, excludeID *uint) (bool, error)
	ListIDsByProjectAndKandang(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error)
	GetTotalPendingChickinQtyByProductWarehouseID(ctx context.Context, productWarehouseID uint) (float64, error)
	WithTx(tx *gorm.DB) ProjectFlockKandangRepository
	DB() *gorm.DB
	IdExists(ctx context.Context, id uint) (bool, error)
}

type projectFlockKandangRepositoryImpl struct {
	db *gorm.DB
}

type ProjectFlockKandangNameWithPeriode struct {
	Id          uint   `gorm:"column:id"`
	KandangName string `gorm:"column:kandang_name"`
	Period      int    `gorm:"column:period"`
}

const flockBaseNameExpression = "LOWER(TRIM(regexp_replace(project_flocks.flock_name, '\\s+\\d+(\\s+\\d+)*$', '', 'g')))"

func NewProjectFlockKandangRepository(db *gorm.DB) ProjectFlockKandangRepository {
	return &projectFlockKandangRepositoryImpl{db: db}
}

func (r *projectFlockKandangRepositoryImpl) CreateMany(ctx context.Context, records []*entity.ProjectFlockKandang) error {
	if len(records) == 0 {
		return nil
	}
	return r.db.WithContext(ctx).Create(&records).Error
}

func (r *projectFlockKandangRepositoryImpl) DeleteMany(ctx context.Context, projectFlockID uint, kandangIDs []uint) error {
	if len(kandangIDs) == 0 {
		return nil
	}
	return r.db.WithContext(ctx).
		Where("project_flock_id = ? AND kandang_id IN ?", projectFlockID, kandangIDs).
		Delete(&entity.ProjectFlockKandang{}).Error
}

func (r *projectFlockKandangRepositoryImpl) GetAll(ctx context.Context, offset int, limit int, modifier func(*gorm.DB) *gorm.DB) ([]entity.ProjectFlockKandang, int64, error) {
	var records []entity.ProjectFlockKandang
	var total int64

	q := r.db.WithContext(ctx)

	if modifier != nil {
		q = modifier(q)
	}

	if err := q.Model(&entity.ProjectFlockKandang{}).Count(&total).Error; err != nil {
		return nil, 0, err
	}

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

	return records, total, nil
}

func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockID(ctx context.Context, projectFlockID uint) ([]entity.ProjectFlockKandang, error) {
	var records []entity.ProjectFlockKandang
	if err := r.db.WithContext(ctx).
		Where("project_flock_id = ?", projectFlockID).
		Preload("Kandang").
		Find(&records).Error; err != nil {
		return nil, err
	}
	return records, nil
}

func (r *projectFlockKandangRepositoryImpl) ListIDsByProjectAndKandang(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error) {
	if len(kandangIDs) == 0 {
		return []uint{}, nil
	}
	var ids []uint
	if err := r.db.WithContext(ctx).
		Model(&entity.ProjectFlockKandang{}).
		Where("project_flock_id = ? AND kandang_id IN ?", projectFlockID, kandangIDs).
		Pluck("id", &ids).Error; err != nil {
		return nil, err
	}
	return ids, nil
}

func (r *projectFlockKandangRepositoryImpl) GetAllWithFilters(ctx context.Context, offset int, limit int, params interface{}) ([]entity.ProjectFlockKandang, int64, error) {
	var records []entity.ProjectFlockKandang
	var total int64

	query, ok := params.(*validation.Query)

	q := r.db.WithContext(ctx).
		Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
		Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
		Preload("ProjectFlock").
		Preload("ProjectFlock.Area").
		Preload("ProjectFlock.Location").
		Preload("ProjectFlock.CreatedUser").
		Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
		Preload("ProjectFlock.Kandangs").
		Preload("ProjectFlock.KandangHistory").
		Preload("Kandang").
		Preload("Chickins").
		Preload("Chickins.CreatedUser").
		Preload("Chickins.ProductWarehouse")

	if ok && query != nil && query.StepName != "" {
		q = q.Where(`
			EXISTS (
				SELECT 1 FROM "approvals"
				WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
				AND "approvals"."approvable_type" = ?
				AND LOWER("approvals"."step_name") = LOWER(?)
				AND "approvals"."id" IN (
					SELECT "approvals"."id" FROM "approvals"
					WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
					AND "approvals"."approvable_type" = ?
					ORDER BY "approvals"."id" DESC
					LIMIT 1
				)
			)
		`, "PROJECT_FLOCK_KANDANGS", query.StepName, "PROJECT_FLOCK_KANDANGS")
	}

	if ok && query != nil {
		if query.Search != "" {
			escapedSearch := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(query.Search)
			q = q.Where(
				r.db.Where("LOWER(\"kandangs\".\"name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%").
					Or("LOWER(\"project_flocks\".\"flock_name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%"),
			)
		}

		if query.ProjectFlockId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"project_flock_id\" = ?", query.ProjectFlockId)
		}

		if query.KandangId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"kandang_id\" = ?", query.KandangId)
		}

		if query.Category != "" {
			q = q.Where("\"project_flocks\".\"category\" = ?", query.Category)
		}

		if query.AreaId > 0 {
			q = q.Where("\"project_flocks\".\"area_id\" = ?", query.AreaId)
		}

		if query.LocationId > 0 {
			q = q.Where("\"kandangs\".\"location_id\" = ?", query.LocationId)
		}
	}

	if err := q.Model(&entity.ProjectFlockKandang{}).Count(&total).Error; err != nil {
		return nil, 0, err
	}

	sortBy := "\"project_flock_kandangs\".\"id\" ASC"
	if ok && query != nil && query.SortBy != "" {
		sortOrder := "DESC"
		if query.SortOrder == "ASC" {
			sortOrder = "ASC"
		}

		switch query.SortBy {
		case "created_at":
			sortBy = "\"project_flock_kandangs\".\"created_at\" " + sortOrder
		case "period":
			sortBy = "\"project_flocks\".\"period\" " + sortOrder
		}
	}

	if err := q.Order(sortBy).Offset(offset).Limit(limit).Find(&records).Error; err != nil {
		return nil, 0, err
	}

	return records, total, nil
}

func (r *projectFlockKandangRepositoryImpl) GetAllWithFiltersScoped(ctx context.Context, offset int, limit int, params interface{}, locationIDs []uint, restrict bool) ([]entity.ProjectFlockKandang, int64, error) {
	var records []entity.ProjectFlockKandang
	var total int64

	query, ok := params.(*validation.Query)

	q := r.db.WithContext(ctx).
		Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
		Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
		Preload("ProjectFlock").
		Preload("ProjectFlock.Area").
		Preload("ProjectFlock.Location").
		Preload("ProjectFlock.CreatedUser").
		Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
		Preload("ProjectFlock.Kandangs").
		Preload("ProjectFlock.KandangHistory").
		Preload("Kandang").
		Preload("Chickins").
		Preload("Chickins.CreatedUser").
		Preload("Chickins.ProductWarehouse")

	if restrict {
		if len(locationIDs) == 0 {
			return []entity.ProjectFlockKandang{}, 0, nil
		}
		q = q.Where("\"project_flocks\".\"location_id\" IN ?", locationIDs)
	}

	if ok && query != nil && query.StepName != "" {
		q = q.Where(`
			EXISTS (
				SELECT 1 FROM "approvals"
				WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
				AND "approvals"."approvable_type" = ?
				AND LOWER("approvals"."step_name") = LOWER(?)
				AND "approvals"."id" IN (
					SELECT "approvals"."id" FROM "approvals"
					WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
					AND "approvals"."approvable_type" = ?
					ORDER BY "approvals"."id" DESC
					LIMIT 1
				)
			)
		`, "PROJECT_FLOCK_KANDANGS", query.StepName, "PROJECT_FLOCK_KANDANGS")
	}

	if ok && query != nil {
		if query.Search != "" {
			escapedSearch := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(query.Search)
			q = q.Where(
				r.db.Where("LOWER(\"kandangs\".\"name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%").
					Or("LOWER(\"project_flocks\".\"flock_name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%"),
			)
		}

		if query.ProjectFlockId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"project_flock_id\" = ?", query.ProjectFlockId)
		}

		if query.KandangId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"kandang_id\" = ?", query.KandangId)
		}

		if query.Category != "" {
			q = q.Where("\"project_flocks\".\"category\" = ?", query.Category)
		}

		if query.AreaId > 0 {
			q = q.Where("\"project_flocks\".\"area_id\" = ?", query.AreaId)
		}

		if query.LocationId > 0 {
			q = q.Where("\"kandangs\".\"location_id\" = ?", query.LocationId)
		}
	}

	if err := q.Model(&entity.ProjectFlockKandang{}).Count(&total).Error; err != nil {
		return nil, 0, err
	}

	sortBy := "\"project_flock_kandangs\".\"id\" ASC"
	if ok && query != nil && query.SortBy != "" {
		sortOrder := "DESC"
		if query.SortOrder == "ASC" {
			sortOrder = "ASC"
		}

		switch query.SortBy {
		case "created_at":
			sortBy = "\"project_flock_kandangs\".\"created_at\" " + sortOrder
		case "period":
			sortBy = "\"project_flocks\".\"period\" " + sortOrder
		}
	}

	if err := q.Order(sortBy).Offset(offset).Limit(limit).Find(&records).Error; err != nil {
		return nil, 0, err
	}

	return records, total, nil
}

func (r *projectFlockKandangRepositoryImpl) GetAllNameWithPeriodeScoped(ctx context.Context, offset int, limit int, params *validation.Query, locationIDs []uint, restrict bool) ([]ProjectFlockKandangNameWithPeriode, int64, error) {
	var records []ProjectFlockKandangNameWithPeriode
	var total int64

	q := r.db.WithContext(ctx).
		Table("project_flock_kandangs").
		Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
		Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"")

	if restrict {
		if len(locationIDs) == 0 {
			return []ProjectFlockKandangNameWithPeriode{}, 0, nil
		}
		q = q.Where("\"project_flocks\".\"location_id\" IN ?", locationIDs)
	}

	if params != nil && params.StepName != "" {
		q = q.Where(`
			EXISTS (
				SELECT 1 FROM "approvals"
				WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
				AND "approvals"."approvable_type" = ?
				AND LOWER("approvals"."step_name") = LOWER(?)
				AND "approvals"."id" IN (
					SELECT "approvals"."id" FROM "approvals"
					WHERE "approvals"."approvable_id" = "project_flock_kandangs"."id"
					AND "approvals"."approvable_type" = ?
					ORDER BY "approvals"."id" DESC
					LIMIT 1
				)
			)
		`, "PROJECT_FLOCK_KANDANGS", params.StepName, "PROJECT_FLOCK_KANDANGS")
	}

	if params != nil {
		if params.Search != "" {
			escapedSearch := strings.NewReplacer("\\", "\\\\", "%", "\\%", "_", "\\_").Replace(params.Search)
			q = q.Where(
				r.db.Where("LOWER(\"kandangs\".\"name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%").
					Or("LOWER(\"project_flocks\".\"flock_name\") LIKE LOWER(?) ESCAPE '\\'", "%"+escapedSearch+"%"),
			)
		}

		if params.ProjectFlockId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"project_flock_id\" = ?", params.ProjectFlockId)
		}

		if params.KandangId > 0 {
			q = q.Where("\"project_flock_kandangs\".\"kandang_id\" = ?", params.KandangId)
		}

		if params.Category != "" {
			q = q.Where("\"project_flocks\".\"category\" = ?", params.Category)
		}

		if params.AreaId > 0 {
			q = q.Where("\"project_flocks\".\"area_id\" = ?", params.AreaId)
		}

		if params.LocationId > 0 {
			q = q.Where("\"kandangs\".\"location_id\" = ?", params.LocationId)
		}
	}

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

	sortBy := "\"project_flock_kandangs\".\"created_at\" DESC"
	if params != nil && params.SortBy != "" {
		sortOrder := "DESC"
		if params.SortOrder == "ASC" {
			sortOrder = "ASC"
		}

		switch params.SortBy {
		case "created_at":
			sortBy = "\"project_flock_kandangs\".\"created_at\" " + sortOrder
		case "period":
			sortBy = "\"project_flocks\".\"period\" " + sortOrder
		}
	}

	if err := q.
		Select("\"project_flock_kandangs\".\"id\", \"project_flock_kandangs\".\"period\", \"kandangs\".\"name\" AS kandang_name").
		Order(sortBy).
		Offset(offset).
		Limit(limit).
		Scan(&records).Error; err != nil {
		return nil, 0, err
	}

	return records, total, nil
}

func (r *projectFlockKandangRepositoryImpl) WithTx(tx *gorm.DB) ProjectFlockKandangRepository {
	return &projectFlockKandangRepositoryImpl{db: tx}
}

func (r *projectFlockKandangRepositoryImpl) DB() *gorm.DB {
	return r.db
}
func (r *projectFlockKandangRepositoryImpl) IdExists(ctx context.Context, id uint) (bool, error) {
	return repository.Exists[entity.ProjectFlockKandang](ctx, r.db, id)
}

func (r *projectFlockKandangRepositoryImpl) GetTotalPendingChickinQtyByProductWarehouseID(ctx context.Context, productWarehouseID uint) (float64, error) {
	var total float64
	err := r.db.WithContext(ctx).
		Table("project_chickins").
		Select("COALESCE(SUM(pending_usage_qty), 0)").
		Where("product_warehouse_id = ?", productWarehouseID).
		Scan(&total).Error
	if err != nil {
		return 0, err
	}
	return total, nil
}

func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
	record := new(entity.ProjectFlockKandang)
	if err := r.db.WithContext(ctx).
		Preload("ProjectFlock").
		Preload("ProjectFlock.Area").
		Preload("ProjectFlock.Location").
		Preload("ProjectFlock.CreatedUser").
		Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
		Preload("ProjectFlock.Kandangs").
		Preload("ProjectFlock.KandangHistory").
		Preload("Kandang").
		Preload("Chickins").
		Preload("Chickins.CreatedUser").
		Preload("Chickins.ProductWarehouse").
		Preload("Chickins.ProductWarehouse.Product").
		Preload("Chickins.ProductWarehouse.Warehouse").
		First(record, id).Error; err != nil {
		return nil, err
	}
	return record, nil
}

// GetByIDLight loads only the minimal relations needed for recording flows.
func (r *projectFlockKandangRepositoryImpl) GetByIDLight(ctx context.Context, id uint) (*entity.ProjectFlockKandang, error) {
	record := new(entity.ProjectFlockKandang)
	if err := r.db.WithContext(ctx).
		Preload("Kandang").
		Preload("Kandang.Location").
		Preload("ProjectFlock").
		Preload("ProjectFlock.Location").
		First(record, id).Error; err != nil {
		return nil, err
	}
	return record, nil
}

func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx context.Context, projectFlockID uint, kandangID uint) (*entity.ProjectFlockKandang, error) {
	record := new(entity.ProjectFlockKandang)
	if err := r.db.WithContext(ctx).
		Where("project_flock_id = ? AND kandang_id = ?", projectFlockID, kandangID).
		Preload("ProjectFlock").
		Preload("ProjectFlock.Area").
		Preload("ProjectFlock.Location").
		Preload("ProjectFlock.CreatedUser").
		Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
		Preload("ProjectFlock.Kandangs").
		Preload("ProjectFlock.KandangHistory").
		Preload("Kandang").
		Preload("Chickins").
		Preload("Chickins.CreatedUser").
		Preload("Chickins.ProductWarehouse").
		Preload("Chickins.ProductWarehouse.Product").
		Preload("Chickins.ProductWarehouse.Warehouse").
		First(record).Error; err != nil {
		return nil, err
	}
	return record, nil
}

func (r *projectFlockKandangRepositoryImpl) GetActiveByKandangID(ctx context.Context, kandangID uint) (*entity.ProjectFlockKandang, error) {
	latestApprovalSubQuery := r.db.
		Table("approvals").
		Select("DISTINCT ON (approvable_id) approvable_id, step_name, id").
		Where("approvable_type = ?", "PROJECT_FLOCKS").
		Order("approvable_id, id DESC")

	var pfkID uint
	if err := r.db.WithContext(ctx).
		Model(&entity.ProjectFlockKandang{}).
		Joins("JOIN project_flocks ON project_flocks.id = project_flock_kandangs.project_flock_id").
		Joins("JOIN (?) AS latest_approval ON latest_approval.approvable_id = project_flocks.id", latestApprovalSubQuery).
		Where("project_flock_kandangs.kandang_id = ?", kandangID).
		Where("project_flock_kandangs.closed_at IS NULL").
		Where("LOWER(latest_approval.step_name) = LOWER(?)", "Aktif").
		Order("project_flock_kandangs.id DESC").
		Limit(1).
		Pluck("project_flock_kandangs.id", &pfkID).Error; err != nil {
		return nil, err
	}

	if pfkID == 0 {
		return nil, gorm.ErrRecordNotFound
	}

	return r.GetByID(ctx, pfkID)
}

func (r *projectFlockKandangRepositoryImpl) UpdateClosedAt(ctx context.Context, id uint, t *time.Time) error {
	return r.db.WithContext(ctx).
		Model(&entity.ProjectFlockKandang{}).
		Where("id = ?", id).
		Update("closed_at", t).Error
}

func (r *projectFlockKandangRepositoryImpl) HasOpenNewerPeriod(ctx context.Context, kandangID uint, currentPeriod int, excludeID *uint) (bool, error) {
	if kandangID == 0 {
		return false, nil
	}
	q := r.db.WithContext(ctx).
		Table("project_flock_kandangs").
		Where("kandang_id = ?", kandangID).
		Where("period > ?", currentPeriod).
		Where("closed_at IS NULL")
	if excludeID != nil {
		q = q.Where("id <> ?", *excludeID)
	}
	var count int64
	if err := q.Count(&count).Error; err != nil {
		return false, err
	}
	return count > 0, nil
}

func (r *projectFlockKandangRepositoryImpl) ListExistingKandangIDs(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]uint, error) {
	if len(kandangIDs) == 0 {
		return nil, nil
	}
	var existing []uint
	err := r.db.WithContext(ctx).
		Table("project_flock_kandangs").
		Where("project_flock_id = ? AND kandang_id IN ?", projectFlockID, kandangIDs).
		Pluck("kandang_id", &existing).Error
	return existing, err
}

func (r *projectFlockKandangRepositoryImpl) HasKandangsLinkedToOtherProject(ctx context.Context, kandangIDs []uint, exceptProjectID *uint) (bool, error) {
	if len(kandangIDs) == 0 {
		return false, nil
	}
	q := r.db.WithContext(ctx).
		Table("project_flock_kandangs").
		Where("kandang_id IN ?", kandangIDs).
		Where("closed_at IS NULL")
	if exceptProjectID != nil {
		q = q.Where("project_flock_id <> ?", *exceptProjectID)
	}
	var count int64
	if err := q.Count(&count).Error; err != nil {
		return false, err
	}
	return count > 0, nil
}

func (r *projectFlockKandangRepositoryImpl) FindKandangsWithRecordings(ctx context.Context, projectFlockID uint, kandangIDs []uint) ([]entity.Kandang, error) {
	if len(kandangIDs) == 0 {
		return nil, nil
	}
	var kandangs []entity.Kandang
	err := r.db.WithContext(ctx).
		Table("recordings AS r").
		Select("pfk.kandang_id AS id, COALESCE(k.name, '') AS name").
		Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
		Joins("LEFT JOIN kandangs AS k ON k.id = pfk.kandang_id").
		Where("pfk.project_flock_id = ? AND pfk.kandang_id IN ?", projectFlockID, kandangIDs).
		Group("pfk.kandang_id, k.name").
		Scan(&kandangs).Error
	return kandangs, err
}

func (r *projectFlockKandangRepositoryImpl) MaxPeriodByBaseName(ctx context.Context, baseName string) (int, error) {
	if strings.TrimSpace(baseName) == "" {
		return 0, nil
	}
	var max int
	err := r.db.WithContext(ctx).
		Table("project_flock_kandangs pfk").
		Joins("JOIN project_flocks pf ON pf.id = pfk.project_flock_id").
		Where(flockBaseNameExpression+" = LOWER(?)", baseName).
		Select("COALESCE(MAX(pfk.period), 0)").
		Scan(&max).Error
	return max, err
}

func (r *projectFlockKandangRepositoryImpl) ProjectPeriodsByProjectIDs(ctx context.Context, projectIDs []uint) (map[uint]int, error) {
	result := make(map[uint]int)
	if len(projectIDs) == 0 {
		return result, nil
	}

	type row struct {
		ProjectFlockID uint
		Period         int
	}
	var rows []row
	if err := r.db.WithContext(ctx).
		Table("project_flock_kandangs").
		Where("project_flock_id IN ?", projectIDs).
		Select("project_flock_id, COALESCE(MAX(period), 0) AS period").
		Group("project_flock_id").
		Scan(&rows).Error; err != nil {
		return nil, err
	}

	for _, item := range rows {
		result[item.ProjectFlockID] = item.Period
	}
	return result, nil
}
