package service

import (
	"context"
	"time"

	commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
)

const (
	hppV2ComponentPakan                = "PAKAN"
	hppV2ComponentOvk                  = "OVK"
	hppV2ComponentDocChickin           = "DOC_CHICKIN"
	hppV2ComponentDirectPulletPurchase = "DIRECT_PULLET_PURCHASE"
	hppV2ComponentBopRegular           = "BOP_REGULAR"
	hppV2ComponentBopEksp              = "BOP_EKSPEDISI"
	hppV2ComponentManualPulletCost     = "MANUAL_PULLET_COST"
	hppV2ComponentRecordingStockRoute  = "RECORDING_STOCK_ROUTE"
	hppV2ComponentDepreciation         = "DEPRECIATION"
	hppV2PartGrowingNormal             = "growing_normal"
	hppV2PartGrowingCutover            = "growing_cutover"
	hppV2PartLayingNormal              = "laying_normal"
	hppV2PartLayingCutover             = "laying_cutover"
	hppV2PartGrowingDirect             = "growing_direct"
	hppV2PartGrowingFarm               = "growing_farm"
	hppV2PartLayingDirect              = "laying_direct"
	hppV2PartLayingFarm                = "laying_farm"
	hppV2PartManualCutover             = "manual_cutover"
	hppV2PartRecordingStockRoute       = "recording_stock_route"
	hppV2PartDepreciationNormal        = "normal_transfer"
	hppV2PartDepreciationCutover       = "manual_cutover"
	hppV2PartDepreciationFarmSnapshot  = "farm_snapshot"
	hppV2ProrationPopulation           = "growing_population_share"
	hppV2ProrationEggWeight            = "laying_egg_weight_share"
	hppV2ProrationEggPiece             = "laying_egg_piece_share"
	hppV2ScopePulletCost               = "pullet_cost"
	hppV2ScopeProductionCost           = "production_cost"
	hppV2CutoverFlagPakan              = string(utils.FlagPakan)
	hppV2CutoverFlagOvk                = "OVK"
)

type HppV2Service interface {
	CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error)
	CalculateHppBreakdown(projectFlockKandangId uint, date *time.Time) (*HppV2Breakdown, error)
	GetCostPakan(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetCostOvk(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetCostDocChickin(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetCostDirectPulletPurchase(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetCostBopRegular(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetCostBopEkspedisi(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetPakanBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetOvkBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetDocChickinBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetDirectPulletPurchaseBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetBopRegularBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetBopEkspedisiBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error)
	GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error)
}

type hppV2Service struct {
	hppRepo commonRepo.HppV2CostRepository
}

type hppV2StockComponentConfig struct {
	Code         string
	Title        string
	NormalFlags  []string
	CutoverFlags []string
}

type hppV2ExpenseComponentConfig struct {
	Code      string
	Title     string
	Ekspedisi bool
}

func NewHppV2Service(hppRepo commonRepo.HppV2CostRepository) HppV2Service {
	return &hppV2Service{hppRepo: hppRepo}
}

func (s *hppV2Service) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
	breakdown, err := s.CalculateHppBreakdown(projectFlockKandangId, date)
	if err != nil {
		return nil, err
	}
	if breakdown == nil {
		return &HppCostResponse{}, nil
	}

	result := breakdown.Hpp
	return &result, nil
}

func (s *hppV2Service) CalculateHppBreakdown(projectFlockKandangId uint, date *time.Time) (*HppV2Breakdown, error) {
	if s.hppRepo == nil {
		return &HppV2Breakdown{
			ProjectFlockKandangID: projectFlockKandangId,
			Hpp:                   HppCostResponse{},
		}, nil
	}

	startOfDay, endOfDay, err := hppV2DayWindow(date)
	if err != nil {
		return nil, err
	}

	contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}

	pakanComponent, err := s.GetPakanBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}

	totalPulletCost := 0.0
	totalProductionCost := 0.0
	components := make([]HppV2Component, 0, 8)
	appendComponent := func(requestedCode string, component *HppV2Component) {
		pulletBefore := totalPulletCost
		productionBefore := totalProductionCost

		if component == nil || (component.Total == 0 && len(component.Parts) == 0) {
			utils.Log.Infof(
				"HPP v2 component skipped: project_flock_kandang_id=%d period_date=%s component=%s reason=empty_or_nil total_pullet_cost=%.2f total_production_cost=%.2f",
				projectFlockKandangId,
				startOfDay.Format("2006-01-02"),
				requestedCode,
				totalPulletCost,
				totalProductionCost,
			)
			return
		}

		pulletAdded := componentScopeTotal(component, hppV2ScopePulletCost)
		productionAdded := componentScopeTotal(component, hppV2ScopeProductionCost)
		components = append(components, *component)
		totalPulletCost += pulletAdded
		totalProductionCost += productionAdded
		utils.Log.Infof(
			"HPP v2 component applied: project_flock_kandang_id=%d period_date=%s component=%s component_total=%.2f pullet_added=%.2f production_added=%.2f total_pullet_before=%.2f total_pullet_after=%.2f total_production_before=%.2f total_production_after=%.2f parts_count=%d",
			projectFlockKandangId,
			startOfDay.Format("2006-01-02"),
			component.Code,
			component.Total,
			pulletAdded,
			productionAdded,
			pulletBefore,
			totalPulletCost,
			productionBefore,
			totalProductionCost,
			len(component.Parts),
		)
	}
	appendComponent(hppV2ComponentPakan, pakanComponent)

	ovkComponent, err := s.GetOvkBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentOvk, ovkComponent)

	docComponent, err := s.GetDocChickinBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentDocChickin, docComponent)

	directPulletComponent, err := s.GetDirectPulletPurchaseBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentDirectPulletPurchase, directPulletComponent)

	bopRegularComponent, err := s.GetBopRegularBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentBopRegular, bopRegularComponent)

	bopEkspedisiComponent, err := s.GetBopEkspedisiBreakdown(projectFlockKandangId, &endOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentBopEksp, bopEkspedisiComponent)

	manualPulletComponent, err := s.getManualPulletCostComponent(projectFlockKandangId, contextRow, startOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentManualPulletCost, manualPulletComponent)

	recordingStockRouteComponent, err := s.getRecordingStockRouteComponent(projectFlockKandangId, contextRow, startOfDay)
	if err != nil {
		return nil, err
	}
	appendComponent(hppV2ComponentRecordingStockRoute, recordingStockRouteComponent)

	depreciationComponent, err := s.getDepreciationComponent(projectFlockKandangId, contextRow, startOfDay, endOfDay, totalPulletCost)
	if err != nil {
		return nil, err
	}

	depreciationCostToProduction := componentScopeTotal(depreciationComponent, hppV2ScopeProductionCost)
	depreciationSource := ""
	if depreciationComponent != nil && len(depreciationComponent.Parts) > 0 {
		depreciationSource = depreciationComponent.Parts[0].Code
	}
	productionCostBeforeDepreciation := totalProductionCost
	appendComponent(hppV2ComponentDepreciation, depreciationComponent)
	utils.Log.Infof(
		"HPP v2 depreciation cost applied: project_flock_kandang_id=%d period_date=%s depreciation_source=%s depreciation_cost=%.2f production_cost_before=%.2f production_cost_after=%.2f",
		projectFlockKandangId,
		startOfDay.Format("2006-01-02"),
		depreciationSource,
		depreciationCostToProduction,
		productionCostBeforeDepreciation,
		totalProductionCost,
	)

	hppCost, err := s.GetHppEstimationDanRealisasi(totalProductionCost, projectFlockKandangId, &startOfDay, &endOfDay)
	if err != nil {
		return nil, err
	}
	if hppCost == nil {
		hppCost = &HppCostResponse{}
	}

	return &HppV2Breakdown{
		ProjectFlockKandangID: projectFlockKandangId,
		ProjectFlockID:        contextRow.ProjectFlockID,
		ProjectFlockCategory:  contextRow.ProjectFlockCategory,
		HouseType:             contextRow.HouseType,
		KandangID:             contextRow.KandangID,
		KandangName:           contextRow.KandangName,
		LocationID:            contextRow.LocationID,
		PeriodDate:            startOfDay.Format("2006-01-02"),
		Window: HppV2DateWindow{
			Start: startOfDay.Format(time.RFC3339),
			End:   endOfDay.Format(time.RFC3339),
		},
		TotalPulletCost:     totalPulletCost,
		TotalProductionCost: totalProductionCost,
		Components:          components,
		Hpp:                 *hppCost,
	}, nil
}

func (s *hppV2Service) GetCostPakan(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetPakanBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetPakanBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	return s.getStockUsageComponent(projectFlockKandangId, endDate, hppV2StockComponentConfig{
		Code:         hppV2ComponentPakan,
		Title:        "Pakan",
		NormalFlags:  []string{string(utils.FlagPakan)},
		CutoverFlags: []string{hppV2CutoverFlagPakan},
	})
}

func (s *hppV2Service) GetCostOvk(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetOvkBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetOvkBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	return s.getStockUsageComponent(projectFlockKandangId, endDate, hppV2StockComponentConfig{
		Code:  hppV2ComponentOvk,
		Title: "OVK",
		NormalFlags: []string{
			string(utils.FlagOVK),
			string(utils.FlagObat),
			string(utils.FlagVitamin),
			string(utils.FlagKimia),
		},
		CutoverFlags: []string{hppV2CutoverFlagOvk},
	})
}

func (s *hppV2Service) GetCostDocChickin(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetDocChickinBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetCostDirectPulletPurchase(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetDirectPulletPurchaseBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetDocChickinBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	if s.hppRepo == nil {
		return &HppV2Component{
			Code:   hppV2ComponentDocChickin,
			Title:  "DOC Chick-in",
			Scopes: []string{hppV2ScopePulletCost},
			Parts:  []HppV2ComponentPart{},
		}, nil
	}

	contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}

	part, err := s.buildGrowingChickinPart(projectFlockKandangId, contextRow, endDate, []string{string(utils.FlagDOC)}, false, hppV2PartGrowingDirect, "Growing DOC")
	if err != nil {
		return nil, err
	}

	parts := make([]HppV2ComponentPart, 0, 1)
	total := 0.0
	if part != nil {
		parts = append(parts, *part)
		total += part.Total
	}

	return &HppV2Component{
		Code:   hppV2ComponentDocChickin,
		Title:  "DOC Chick-in",
		Scopes: []string{hppV2ScopePulletCost},
		Total:  total,
		Parts:  parts,
	}, nil
}

func (s *hppV2Service) GetDirectPulletPurchaseBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	part, err := s.buildLayingChickinPart(projectFlockKandangId, endDate, []string{string(utils.FlagPullet), string(utils.FlagLayer)}, true, hppV2PartLayingDirect, "Laying Direct Pullet")
	if err != nil {
		return nil, err
	}

	parts := make([]HppV2ComponentPart, 0, 1)
	total := 0.0
	if part != nil {
		parts = append(parts, *part)
		total += part.Total
	}

	return &HppV2Component{
		Code:   hppV2ComponentDirectPulletPurchase,
		Title:  "Direct Pullet Purchase",
		Scopes: []string{hppV2ScopeProductionCost},
		Total:  total,
		Parts:  parts,
	}, nil
}

func (s *hppV2Service) GetCostBopRegular(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetBopRegularBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetCostBopEkspedisi(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	component, err := s.GetBopEkspedisiBreakdown(projectFlockKandangId, endDate)
	if err != nil {
		return 0, err
	}
	if component == nil {
		return 0, nil
	}

	return component.Total, nil
}

func (s *hppV2Service) GetBopRegularBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	return s.getExpenseComponent(projectFlockKandangId, endDate, hppV2ExpenseComponentConfig{
		Code:      hppV2ComponentBopRegular,
		Title:     "BOP Regular",
		Ekspedisi: false,
	})
}

func (s *hppV2Service) GetBopEkspedisiBreakdown(projectFlockKandangId uint, endDate *time.Time) (*HppV2Component, error) {
	return s.getExpenseComponent(projectFlockKandangId, endDate, hppV2ExpenseComponentConfig{
		Code:      hppV2ComponentBopEksp,
		Title:     "BOP Ekspedisi",
		Ekspedisi: true,
	})
}

func (s *hppV2Service) getStockUsageComponent(projectFlockKandangId uint, endDate *time.Time, config hppV2StockComponentConfig) (*HppV2Component, error) {
	if s.hppRepo == nil {
		return &HppV2Component{
			Code:   config.Code,
			Title:  config.Title,
			Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
			Parts:  []HppV2ComponentPart{},
		}, nil
	}

	contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}

	parts := make([]HppV2ComponentPart, 0, 4)
	total := 0.0

	growingPart, err := s.buildGrowingUsagePart(projectFlockKandangId, contextRow, endDate, config, false)
	if err != nil {
		return nil, err
	}
	if growingPart != nil {
		parts = append(parts, *growingPart)
		total += growingPart.Total
	}

	growingCutoverPart, err := s.buildGrowingUsagePart(projectFlockKandangId, contextRow, endDate, config, true)
	if err != nil {
		return nil, err
	}
	if growingCutoverPart != nil {
		parts = append(parts, *growingCutoverPart)
		total += growingCutoverPart.Total
	}

	layingNormalPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, false)
	if err != nil {
		return nil, err
	}
	if layingNormalPart != nil {
		parts = append(parts, *layingNormalPart)
		total += layingNormalPart.Total
	}

	layingCutoverPart, err := s.buildLayingUsagePart(projectFlockKandangId, endDate, config, true)
	if err != nil {
		return nil, err
	}
	if layingCutoverPart != nil {
		parts = append(parts, *layingCutoverPart)
		total += layingCutoverPart.Total
	}

	return &HppV2Component{
		Code:   config.Code,
		Title:  config.Title,
		Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
		Total:  total,
		Parts:  parts,
	}, nil
}

func (s *hppV2Service) getExpenseComponent(projectFlockKandangId uint, endDate *time.Time, config hppV2ExpenseComponentConfig) (*HppV2Component, error) {
	if s.hppRepo == nil {
		return &HppV2Component{
			Code:   config.Code,
			Title:  config.Title,
			Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
			Parts:  []HppV2ComponentPart{},
		}, nil
	}

	contextRow, err := s.hppRepo.GetProjectFlockKandangContext(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}

	parts := make([]HppV2ComponentPart, 0, 4)
	total := 0.0

	growingDirect, err := s.buildGrowingExpenseDirectPart(projectFlockKandangId, contextRow, endDate, config)
	if err != nil {
		return nil, err
	}
	if growingDirect != nil {
		parts = append(parts, *growingDirect)
		total += growingDirect.Total
	}

	growingFarm, err := s.buildGrowingExpenseFarmPart(projectFlockKandangId, contextRow, endDate, config)
	if err != nil {
		return nil, err
	}
	if growingFarm != nil {
		parts = append(parts, *growingFarm)
		total += growingFarm.Total
	}

	layingDirect, err := s.buildLayingExpenseDirectPart(projectFlockKandangId, endDate, config)
	if err != nil {
		return nil, err
	}
	if layingDirect != nil {
		parts = append(parts, *layingDirect)
		total += layingDirect.Total
	}

	layingFarm, err := s.buildLayingExpenseFarmPart(projectFlockKandangId, contextRow, endDate, config)
	if err != nil {
		return nil, err
	}
	if layingFarm != nil {
		parts = append(parts, *layingFarm)
		total += layingFarm.Total
	}

	return &HppV2Component{
		Code:   config.Code,
		Title:  config.Title,
		Scopes: []string{hppV2ScopePulletCost, hppV2ScopeProductionCost},
		Total:  total,
		Parts:  parts,
	}, nil
}

func (s *hppV2Service) buildGrowingChickinPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	flagNames []string,
	excludeTransferToLaying bool,
	partCode string,
	partTitle string,
) (*HppV2ComponentPart, error) {
	if contextRow == nil {
		return nil, nil
	}

	sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}
	if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
		return nil, nil
	}

	kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(kandangIDsGrowing) == 0 {
		return nil, nil
	}

	totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
	if err != nil {
		return nil, err
	}
	if totalPopulationFlockGrowing <= 0 {
		return nil, nil
	}

	ratio := transferTotalQty / totalPopulationFlockGrowing
	if ratio <= 0 {
		return nil, nil
	}

	rows, err := s.hppRepo.ListChickinCostRowsByProductFlags(context.Background(), kandangIDsGrowing, flagNames, endDate, excludeTransferToLaying)
	if err != nil {
		return nil, err
	}

	return buildChickinPartFromRows(
		rows,
		partCode,
		partTitle,
		[]string{hppV2ScopePulletCost},
		&HppV2Proration{
			Basis:       hppV2ProrationPopulation,
			Numerator:   transferTotalQty,
			Denominator: totalPopulationFlockGrowing,
			Ratio:       ratio,
		},
		ratio,
	), nil
}

func (s *hppV2Service) buildLayingChickinPart(
	projectFlockKandangId uint,
	endDate *time.Time,
	flagNames []string,
	excludeTransferToLaying bool,
	partCode string,
	partTitle string,
) (*HppV2ComponentPart, error) {
	rows, err := s.hppRepo.ListChickinCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, flagNames, endDate, excludeTransferToLaying)
	if err != nil {
		return nil, err
	}

	return buildChickinPartFromRows(rows, partCode, partTitle, []string{hppV2ScopeProductionCost}, nil, 1), nil
}

func (s *hppV2Service) buildGrowingUsagePart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	config hppV2StockComponentConfig,
	cutover bool,
) (*HppV2ComponentPart, error) {
	if contextRow == nil {
		return nil, nil
	}

	sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}
	if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
		return nil, nil
	}

	kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(kandangIDsGrowing) == 0 {
		return nil, nil
	}

	totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
	if err != nil {
		return nil, err
	}
	if totalPopulationFlockGrowing == 0 {
		return nil, nil
	}

	ratio := transferTotalQty / totalPopulationFlockGrowing
	if ratio <= 0 {
		return nil, nil
	}

	partCode := hppV2PartGrowingNormal
	partTitle := "Growing"
	baseRows := make([]HppV2Reference, 0)
	baseTotal := 0.0

	if cutover {
		partCode = hppV2PartGrowingCutover
		partTitle = "Growing Cut-over"

		rows, err := s.hppRepo.ListAdjustmentCostRowsByProductFlags(context.Background(), kandangIDsGrowing, config.CutoverFlags, endDate)
		if err != nil {
			return nil, err
		}
		for _, row := range rows {
			rowTotal := adjustmentRowTotalCost(row)
			baseTotal += rowTotal
			baseRows = append(baseRows, HppV2Reference{
				Type:                  "adjustment_stock",
				ID:                    row.AdjustmentID,
				ProjectFlockKandangID: row.ProjectFlockKandangID,
				ProductID:             row.ProductID,
				ProductName:           row.ProductName,
				Date:                  row.CreatedAt.Format("2006-01-02"),
				Qty:                   row.Qty,
				UnitPrice:             row.Price,
				Total:                 rowTotal,
				AppliedTotal:          rowTotal * ratio,
			})
		}
	} else {
		rows, err := s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), kandangIDsGrowing, config.NormalFlags, endDate)
		if err != nil {
			return nil, err
		}
		for _, row := range rows {
			baseTotal += row.TotalCost
			refDate := row.LastUsedAt
			if refDate.IsZero() {
				refDate = row.FirstUsedAt
			}
			baseRows = append(baseRows, HppV2Reference{
				Type:          "stock_allocation",
				ID:            row.StockableID,
				StockableType: row.StockableType,
				ProductID:     row.SourceProductID,
				ProductName:   row.SourceProductName,
				Date:          refDate.Format("2006-01-02"),
				Qty:           row.Qty,
				UnitPrice:     row.UnitPrice,
				Total:         row.TotalCost,
				AppliedTotal:  row.TotalCost * ratio,
			})
		}
	}

	if baseTotal == 0 {
		return nil, nil
	}

	return &HppV2ComponentPart{
		Code:   partCode,
		Title:  partTitle,
		Scopes: []string{hppV2ScopePulletCost},
		Total:  baseTotal * ratio,
		Proration: &HppV2Proration{
			Basis:       hppV2ProrationPopulation,
			Numerator:   transferTotalQty,
			Denominator: totalPopulationFlockGrowing,
			Ratio:       ratio,
		},
		References: baseRows,
	}, nil
}

func (s *hppV2Service) buildLayingUsagePart(
	projectFlockKandangId uint,
	endDate *time.Time,
	config hppV2StockComponentConfig,
	cutover bool,
) (*HppV2ComponentPart, error) {
	if cutover {
		rows, err := s.hppRepo.ListAdjustmentCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.CutoverFlags, endDate)
		if err != nil {
			return nil, err
		}

		total := 0.0
		references := make([]HppV2Reference, 0, len(rows))
		for _, row := range rows {
			rowTotal := adjustmentRowTotalCost(row)
			total += rowTotal
			references = append(references, HppV2Reference{
				Type:                  "adjustment_stock",
				ID:                    row.AdjustmentID,
				ProjectFlockKandangID: row.ProjectFlockKandangID,
				ProductID:             row.ProductID,
				ProductName:           row.ProductName,
				Date:                  row.CreatedAt.Format("2006-01-02"),
				Qty:                   row.Qty,
				UnitPrice:             row.Price,
				Total:                 rowTotal,
				AppliedTotal:          rowTotal,
			})
		}
		if total == 0 {
			return nil, nil
		}

		return &HppV2ComponentPart{
			Code:       hppV2PartLayingCutover,
			Title:      "Laying Cut-over",
			Scopes:     []string{hppV2ScopeProductionCost},
			Total:      total,
			References: references,
		}, nil
	}

	rows, err := s.hppRepo.ListUsageCostRowsByProductFlags(context.Background(), []uint{projectFlockKandangId}, config.NormalFlags, endDate)
	if err != nil {
		return nil, err
	}

	total := 0.0
	references := make([]HppV2Reference, 0, len(rows))
	for _, row := range rows {
		total += row.TotalCost
		refDate := row.LastUsedAt
		if refDate.IsZero() {
			refDate = row.FirstUsedAt
		}
		references = append(references, HppV2Reference{
			Type:          "stock_allocation",
			ID:            row.StockableID,
			StockableType: row.StockableType,
			ProductID:     row.SourceProductID,
			ProductName:   row.SourceProductName,
			Date:          refDate.Format("2006-01-02"),
			Qty:           row.Qty,
			UnitPrice:     row.UnitPrice,
			Total:         row.TotalCost,
			AppliedTotal:  row.TotalCost,
		})
	}
	if total == 0 {
		return nil, nil
	}

	return &HppV2ComponentPart{
		Code:       hppV2PartLayingNormal,
		Title:      "Laying",
		Scopes:     []string{hppV2ScopeProductionCost},
		Total:      total,
		References: references,
	}, nil
}

func (s *hppV2Service) buildGrowingExpenseDirectPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
	return s.buildGrowingExpensePart(projectFlockKandangId, contextRow, endDate, config, false)
}

func (s *hppV2Service) buildGrowingExpenseFarmPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
	return s.buildGrowingExpensePart(projectFlockKandangId, contextRow, endDate, config, true)
}

func (s *hppV2Service) buildGrowingExpensePart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	config hppV2ExpenseComponentConfig,
	farmLevel bool,
) (*HppV2ComponentPart, error) {
	if contextRow == nil {
		return nil, nil
	}

	sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}
	if sourceProjectFlockID == 0 || transferTotalQty <= 0 {
		return nil, nil
	}

	kandangIDsGrowing, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(kandangIDsGrowing) == 0 {
		return nil, nil
	}

	totalPopulationFlockGrowing, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDsGrowing)
	if err != nil {
		return nil, err
	}
	if totalPopulationFlockGrowing <= 0 {
		return nil, nil
	}

	ratio := transferTotalQty / totalPopulationFlockGrowing
	if ratio <= 0 {
		return nil, nil
	}

	var rows []commonRepo.HppV2ExpenseCostRow
	if farmLevel {
		rows, err = s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), sourceProjectFlockID, endDate, config.Ekspedisi)
	} else {
		rows, err = s.hppRepo.ListExpenseRealizationRowsByProjectFlockKandangIDs(context.Background(), kandangIDsGrowing, endDate, config.Ekspedisi)
	}
	if err != nil {
		return nil, err
	}

	return buildExpensePartFromRows(
		rows,
		map[bool]string{false: hppV2PartGrowingDirect, true: hppV2PartGrowingFarm}[farmLevel],
		map[bool]string{false: "Growing Direct", true: "Growing Farm"}[farmLevel],
		[]string{hppV2ScopePulletCost},
		&HppV2Proration{
			Basis:       hppV2ProrationPopulation,
			Numerator:   transferTotalQty,
			Denominator: totalPopulationFlockGrowing,
			Ratio:       ratio,
		},
		ratio,
	), nil
}

func (s *hppV2Service) buildLayingExpenseDirectPart(
	projectFlockKandangId uint,
	endDate *time.Time,
	config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
	rows, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockKandangIDs(context.Background(), []uint{projectFlockKandangId}, endDate, config.Ekspedisi)
	if err != nil {
		return nil, err
	}

	return buildExpensePartFromRows(rows, hppV2PartLayingDirect, "Laying Direct", []string{hppV2ScopeProductionCost}, nil, 1), nil
}

func (s *hppV2Service) buildLayingExpenseFarmPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	endDate *time.Time,
	config hppV2ExpenseComponentConfig,
) (*HppV2ComponentPart, error) {
	if contextRow == nil {
		return nil, nil
	}

	rows, err := s.hppRepo.ListExpenseRealizationRowsByProjectFlockID(context.Background(), contextRow.ProjectFlockID, endDate, config.Ekspedisi)
	if err != nil {
		return nil, err
	}
	if len(rows) == 0 {
		return nil, nil
	}

	farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	targetPieces, targetWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {
		return nil, err
	}
	farmPieces, farmWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), farmPFKIDs, endDate)
	if err != nil {
		return nil, err
	}

	basis := hppV2ProrationEggWeight
	numerator := targetWeight
	denominator := farmWeight
	if denominator <= 0 {
		basis = hppV2ProrationEggPiece
		numerator = targetPieces
		denominator = farmPieces
	}
	if denominator <= 0 {
		return nil, nil
	}

	ratio := numerator / denominator
	if ratio <= 0 {
		return nil, nil
	}

	return buildExpensePartFromRows(
		rows,
		hppV2PartLayingFarm,
		"Laying Farm",
		[]string{hppV2ScopeProductionCost},
		&HppV2Proration{
			Basis:       basis,
			Numerator:   numerator,
			Denominator: denominator,
			Ratio:       ratio,
		},
		ratio,
	), nil
}

func (s *hppV2Service) getManualPulletCostComponent(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	periodDate time.Time,
) (*HppV2Component, error) {
	if s.hppRepo == nil || contextRow == nil {
		return nil, nil
	}

	sourceProjectFlockID, transferTotalQty, err := s.hppRepo.GetTransferSourceSummary(context.Background(), projectFlockKandangId)
	if err != nil {
		return nil, err
	}
	if sourceProjectFlockID != 0 && transferTotalQty > 0 {
		return nil, nil
	}

	manualInput, err := s.hppRepo.GetManualDepreciationInputByProjectFlockID(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if manualInput == nil || manualInput.TotalCost <= 0 || manualInput.CutoverDate.IsZero() {
		return nil, nil
	}
	if dateOnly(periodDate).Before(dateOnly(manualInput.CutoverDate)) {
		return nil, nil
	}

	farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(farmPFKIDs) == 0 {
		return nil, nil
	}

	totalPopulation, err := s.hppRepo.GetTotalPopulation(context.Background(), farmPFKIDs)
	if err != nil {
		return nil, err
	}
	if totalPopulation <= 0 {
		return nil, nil
	}

	targetPopulation, err := s.hppRepo.GetTotalPopulation(context.Background(), []uint{projectFlockKandangId})
	if err != nil {
		return nil, err
	}
	if targetPopulation <= 0 {
		return nil, nil
	}

	ratio := targetPopulation / totalPopulation
	if ratio <= 0 {
		return nil, nil
	}

	appliedTotal := manualInput.TotalCost * ratio
	part := HppV2ComponentPart{
		Code:   hppV2PartManualCutover,
		Title:  "Manual Cut-over",
		Scopes: []string{hppV2ScopePulletCost},
		Total:  appliedTotal,
		Proration: &HppV2Proration{
			Basis:       hppV2ProrationPopulation,
			Numerator:   targetPopulation,
			Denominator: totalPopulation,
			Ratio:       ratio,
		},
		Details: map[string]any{
			"cutover_date":      formatDateOnly(manualInput.CutoverDate),
			"farm_total_cost":   manualInput.TotalCost,
			"target_population": targetPopulation,
			"farm_population":   totalPopulation,
		},
		References: []HppV2Reference{
			{
				Type:         "farm_depreciation_manual_input",
				ID:           manualInput.ID,
				Date:         formatDateOnly(manualInput.CutoverDate),
				Qty:          1,
				Total:        manualInput.TotalCost,
				AppliedTotal: appliedTotal,
			},
		},
	}

	return &HppV2Component{
		Code:   hppV2ComponentManualPulletCost,
		Title:  "Manual Pullet Cost",
		Scopes: []string{hppV2ScopePulletCost},
		Total:  appliedTotal,
		Parts:  []HppV2ComponentPart{part},
	}, nil
}

func (s *hppV2Service) getRecordingStockRouteComponent(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	periodDate time.Time,
) (*HppV2Component, error) {
	if s.hppRepo == nil || contextRow == nil || periodDate.IsZero() {
		return nil, nil
	}

	farmTotalCost, err := s.hppRepo.GetRecordingStockRoutingAdjustmentCostByProjectFlockID(
		context.Background(),
		contextRow.ProjectFlockID,
		periodDate,
	)
	if err != nil {
		return nil, err
	}
	if farmTotalCost <= 0 {
		return nil, nil
	}

	farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(farmPFKIDs) == 0 {
		return nil, nil
	}

	totalPopulation, err := s.hppRepo.GetTotalPopulation(context.Background(), farmPFKIDs)
	if err != nil {
		return nil, err
	}
	if totalPopulation <= 0 {
		return nil, nil
	}

	targetPopulation, err := s.hppRepo.GetTotalPopulation(context.Background(), []uint{projectFlockKandangId})
	if err != nil {
		return nil, err
	}
	if targetPopulation <= 0 {
		return nil, nil
	}

	ratio := targetPopulation / totalPopulation
	if ratio <= 0 {
		return nil, nil
	}

	appliedTotal := farmTotalCost * ratio
	if appliedTotal <= 0 {
		return nil, nil
	}

	part := HppV2ComponentPart{
		Code:   hppV2PartRecordingStockRoute,
		Title:  "Recording Stock Route",
		Scopes: []string{hppV2ScopePulletCost},
		Total:  appliedTotal,
		Proration: &HppV2Proration{
			Basis:       hppV2ProrationPopulation,
			Numerator:   targetPopulation,
			Denominator: totalPopulation,
			Ratio:       ratio,
		},
		Details: map[string]any{
			"period_date":              formatDateOnly(periodDate),
			"farm_total_cost":          farmTotalCost,
			"target_population":        targetPopulation,
			"farm_population":          totalPopulation,
			"project_flock_id":         contextRow.ProjectFlockID,
			"project_flock_kandang_id": projectFlockKandangId,
		},
		References: []HppV2Reference{
			{
				Type:         "recording_stock_route",
				Date:         formatDateOnly(periodDate),
				Qty:          1,
				Total:        farmTotalCost,
				AppliedTotal: appliedTotal,
			},
		},
	}

	return &HppV2Component{
		Code:   hppV2ComponentRecordingStockRoute,
		Title:  "Recording Stock Route",
		Scopes: []string{hppV2ScopePulletCost},
		Total:  appliedTotal,
		Parts:  []HppV2ComponentPart{part},
	}, nil
}

func (s *hppV2Service) getDepreciationComponent(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	periodDate time.Time,
	endDate time.Time,
	totalPulletCost float64,
) (*HppV2Component, error) {
	if s.hppRepo == nil || contextRow == nil {
		return nil, nil
	}

	snapshotPart, err := s.buildFarmSnapshotDepreciationPart(projectFlockKandangId, contextRow, periodDate, endDate)
	if err != nil {
		return nil, err
	}
	if snapshotPart != nil {
		return &HppV2Component{
			Code:   hppV2ComponentDepreciation,
			Title:  "Depreciation",
			Scopes: []string{hppV2ScopeProductionCost},
			Total:  snapshotPart.Total,
			Parts:  []HppV2ComponentPart{*snapshotPart},
		}, nil
	}

	if totalPulletCost <= 0 {
		return nil, nil
	}

	transferInput, err := s.hppRepo.GetLatestTransferInputByProjectFlockKandangID(context.Background(), projectFlockKandangId, periodDate)
	if err != nil {
		return nil, err
	}

	var part *HppV2ComponentPart
	if transferInput != nil && transferInput.SourceProjectFlockID > 0 {
		part, err = s.buildNormalTransferDepreciationPart(contextRow, transferInput, periodDate, totalPulletCost)
		if err != nil {
			return nil, err
		}
	} else {
		part, err = s.buildManualCutoverDepreciationPart(projectFlockKandangId, contextRow, periodDate, totalPulletCost)
		if err != nil {
			return nil, err
		}
	}
	if part == nil {
		return nil, nil
	}

	return &HppV2Component{
		Code:   hppV2ComponentDepreciation,
		Title:  "Depreciation",
		Scopes: []string{hppV2ScopeProductionCost},
		Total:  part.Total,
		Parts:  []HppV2ComponentPart{*part},
	}, nil
}

func (s *hppV2Service) buildFarmSnapshotDepreciationPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	periodDate time.Time,
	endDate time.Time,
) (*HppV2ComponentPart, error) {
	if contextRow == nil {
		return nil, nil
	}

	snapshot, err := s.hppRepo.GetFarmDepreciationSnapshotByProjectFlockIDAndPeriod(context.Background(), contextRow.ProjectFlockID, periodDate)
	if err != nil {
		return nil, err
	}
	if snapshot == nil || snapshot.DepreciationValue <= 0 {
		return nil, nil
	}

	farmPFKIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if len(farmPFKIDs) == 0 {
		return nil, nil
	}

	end := endDate
	targetPieces, targetWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, &end)
	if err != nil {
		return nil, err
	}
	farmPieces, farmWeight, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), farmPFKIDs, &end)
	if err != nil {
		return nil, err
	}

	basis := hppV2ProrationEggWeight
	numerator := targetWeight
	denominator := farmWeight
	if denominator <= 0 {
		basis = hppV2ProrationEggPiece
		numerator = targetPieces
		denominator = farmPieces
	}
	if denominator <= 0 {
		return nil, nil
	}

	ratio := numerator / denominator
	if ratio <= 0 {
		return nil, nil
	}

	appliedDepreciation := snapshot.DepreciationValue * ratio
	if appliedDepreciation <= 0 {
		return nil, nil
	}
	appliedPulletCostDayN := snapshot.PulletCostDayNTotal * ratio
	depreciationPercent := snapshot.DepreciationPercentEffective
	if appliedPulletCostDayN > 0 {
		depreciationPercent = (appliedDepreciation / appliedPulletCostDayN) * 100
	}

	return &HppV2ComponentPart{
		Code:   hppV2PartDepreciationFarmSnapshot,
		Title:  "Farm Snapshot",
		Scopes: []string{hppV2ScopeProductionCost},
		Total:  appliedDepreciation,
		Proration: &HppV2Proration{
			Basis:       basis,
			Numerator:   numerator,
			Denominator: denominator,
			Ratio:       ratio,
		},
		Details: map[string]any{
			"basis_total":            snapshot.DepreciationValue,
			"pullet_cost_day_n":      appliedPulletCostDayN,
			"depreciation_percent":   depreciationPercent,
			"snapshot_id":            snapshot.ID,
			"snapshot_period_date":   formatDateOnly(snapshot.PeriodDate),
			"snapshot_project_flock": snapshot.ProjectFlockID,
		},
		References: []HppV2Reference{
			{
				Type:         "farm_depreciation_snapshot",
				ID:           snapshot.ID,
				Date:         formatDateOnly(snapshot.PeriodDate),
				Qty:          1,
				Total:        snapshot.DepreciationValue,
				AppliedTotal: appliedDepreciation,
			},
		},
	}, nil
}

func (s *hppV2Service) buildNormalTransferDepreciationPart(
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	transferInput *commonRepo.HppV2LatestTransferInputRow,
	periodDate time.Time,
	totalPulletCost float64,
) (*HppV2ComponentPart, error) {
	if contextRow == nil || transferInput == nil || totalPulletCost <= 0 {
		return nil, nil
	}

	originDate, err := s.hppRepo.GetEarliestChickInDateByProjectFlockID(context.Background(), transferInput.SourceProjectFlockID)
	if err != nil {
		return nil, err
	}
	if originDate == nil || originDate.IsZero() {
		return nil, nil
	}

	scheduleDay := DepreciationScheduleDay(*originDate, periodDate, contextRow.HouseType)
	if scheduleDay <= 0 {
		return nil, nil
	}

	houseType := NormalizeDepreciationHouseType(contextRow.HouseType)
	percentByHouseType, err := s.hppRepo.GetDepreciationPercents(context.Background(), []string{houseType}, scheduleDay)
	if err != nil {
		return nil, err
	}

	pulletCostDayN, depreciationValue, depreciationPercent := CalculateDepreciationAtDayN(
		totalPulletCost,
		scheduleDay,
		contextRow.HouseType,
		percentByHouseType,
	)
	if depreciationValue <= 0 {
		return nil, nil
	}

	return &HppV2ComponentPart{
		Code:   hppV2PartDepreciationNormal,
		Title:  "Normal Transfer",
		Scopes: []string{hppV2ScopeProductionCost},
		Total:  depreciationValue,
		Details: map[string]any{
			"basis_total":             totalPulletCost,
			"pullet_cost_day_n":       pulletCostDayN,
			"depreciation_percent":    depreciationPercent,
			"schedule_day":            scheduleDay,
			"origin_date":             formatDateOnly(*originDate),
			"transfer_date":           formatDateOnly(transferInput.TransferDate),
			"source_project_flock_id": transferInput.SourceProjectFlockID,
		},
		References: []HppV2Reference{
			{
				Type:         "laying_transfer",
				ID:           transferInput.TransferID,
				Date:         formatDateOnly(transferInput.TransferDate),
				Qty:          transferInput.TransferQty,
				Total:        totalPulletCost,
				AppliedTotal: depreciationValue,
			},
		},
	}, nil
}

func (s *hppV2Service) buildManualCutoverDepreciationPart(
	projectFlockKandangId uint,
	contextRow *commonRepo.HppV2ProjectFlockKandangContext,
	periodDate time.Time,
	totalPulletCost float64,
) (*HppV2ComponentPart, error) {
	if contextRow == nil || totalPulletCost <= 0 {
		return nil, nil
	}

	manualInput, err := s.hppRepo.GetManualDepreciationInputByProjectFlockID(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if manualInput == nil || manualInput.TotalCost <= 0 || manualInput.CutoverDate.IsZero() {
		return nil, nil
	}
	if dateOnly(periodDate).Before(dateOnly(manualInput.CutoverDate)) {
		return nil, nil
	}

	originDate, err := s.hppRepo.GetEarliestChickInDateByProjectFlockID(context.Background(), contextRow.ProjectFlockID)
	if err != nil {
		return nil, err
	}
	if originDate == nil || originDate.IsZero() {
		return nil, nil
	}

	reportScheduleDay := DepreciationScheduleDay(*originDate, periodDate, contextRow.HouseType)
	if reportScheduleDay <= 0 {
		return nil, nil
	}

	cutoverScheduleDay := DepreciationScheduleDay(*originDate, manualInput.CutoverDate, contextRow.HouseType)
	startDay := 1
	if cutoverScheduleDay > 0 {
		startDay = cutoverScheduleDay
	}

	houseType := NormalizeDepreciationHouseType(contextRow.HouseType)
	percentByHouseType, err := s.hppRepo.GetDepreciationPercents(context.Background(), []string{houseType}, reportScheduleDay)
	if err != nil {
		return nil, err
	}

	pulletCostDayN, depreciationValue, depreciationPercent := CalculateDepreciationFromDayRange(
		totalPulletCost,
		startDay,
		reportScheduleDay,
		contextRow.HouseType,
		percentByHouseType,
	)
	if depreciationValue <= 0 {
		return nil, nil
	}

	return &HppV2ComponentPart{
		Code:   hppV2PartDepreciationCutover,
		Title:  "Manual Cut-over",
		Scopes: []string{hppV2ScopeProductionCost},
		Total:  depreciationValue,
		Details: map[string]any{
			"basis_total":           totalPulletCost,
			"pullet_cost_day_n":     pulletCostDayN,
			"depreciation_percent":  depreciationPercent,
			"schedule_day":          reportScheduleDay,
			"start_schedule_day":    startDay,
			"origin_date":           formatDateOnly(*originDate),
			"cutover_date":          formatDateOnly(manualInput.CutoverDate),
			"manual_input_id":       manualInput.ID,
			"project_flock_kandang": projectFlockKandangId,
		},
		References: []HppV2Reference{
			{
				Type:         "farm_depreciation_manual_input",
				ID:           manualInput.ID,
				Date:         formatDateOnly(manualInput.CutoverDate),
				Qty:          1,
				Total:        totalPulletCost,
				AppliedTotal: depreciationValue,
			},
		},
	}, nil
}

func (s *hppV2Service) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) {
	utils.Log.Infof(
		"GetHppEstimationDanRealisasi started: project_flock_kandang_id=%d total_production_cost=%.2f start_date=%s end_date=%s",
		projectFlockKandangId,
		totalProductionCost,
		formatTimePtr(startDate),
		formatTimePtr(endDate),
	)

	if s.hppRepo == nil {
		utils.Log.Warnf(
			"GetHppEstimationDanRealisasi skipped: hpp repository is nil (project_flock_kandang_id=%d)",
			projectFlockKandangId,
		)
		return &HppCostResponse{}, nil
	}

	recordingQty, recordingWeight, adjustmentQty, adjustmentWeight, err := s.hppRepo.GetEggProduksiBreakdownByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {
		utils.Log.WithError(err).Errorf(
			"GetHppEstimationDanRealisasi failed to get estimation egg production: project_flock_kandang_id=%d end_date=%s",
			projectFlockKandangId,
			formatTimePtr(endDate),
		)
		return nil, err
	}
	estimPieces := recordingQty + adjustmentQty
	estimWeightKg := recordingWeight + adjustmentWeight

	realPieces, realWeightKg, err := s.hppRepo.GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, startDate, endDate)
	if err != nil {
		utils.Log.WithError(err).Errorf(
			"GetHppEstimationDanRealisasi failed to get realization egg sales: project_flock_kandang_id=%d start_date=%s end_date=%s",
			projectFlockKandangId,
			formatTimePtr(startDate),
			formatTimePtr(endDate),
		)
		return nil, err
	}

	estimation := HppCostDetail{
		Total: totalProductionCost,
		Kg:    estimWeightKg,
		Butir: estimPieces,
	}
	if estimWeightKg > 0 {
		estimation.HargaKg = roundToTwoDecimals(totalProductionCost / estimWeightKg)
	}
	if estimPieces > 0 {
		estimation.HargaButir = roundToTwoDecimals(totalProductionCost / estimPieces)
	}

	real := HppCostDetail{
		Total: totalProductionCost,
		Kg:    realWeightKg,
		Butir: realPieces,
	}
	if realWeightKg > 0 {
		real.HargaKg = roundToTwoDecimals(totalProductionCost / realWeightKg)
	}
	if realPieces > 0 {
		real.HargaButir = roundToTwoDecimals(totalProductionCost / realPieces)
	}

	utils.Log.Infof(
		"GetHppEstimationDanRealisasi success: project_flock_kandang_id=%d estimation_butir=%.2f estimation_kg=%.2f estimation_harga_butir=%.2f estimation_harga_kg=%.2f real_butir=%.2f real_kg=%.2f real_harga_butir=%.2f real_harga_kg=%.2f totalProductionCost=%.2f",
		projectFlockKandangId,
		estimation.Butir,
		estimation.Kg,
		estimation.HargaButir,
		estimation.HargaKg,
		real.Butir,
		real.Kg,
		real.HargaButir,
		real.HargaKg,
		totalProductionCost,
	)

	return &HppCostResponse{
		Estimation: estimation,
		Real:       real,
		DebugValues: &HppCostDebugValues{
			RecordingEggQty:     recordingQty,
			RecordingEggWeight:  recordingWeight,
			AdjustmentEggQty:    adjustmentQty,
			AdjustmentEggWeight: adjustmentWeight,
			SoldEggQty:          realPieces,
			SoldEggWeight:       realWeightKg,
		},
	}, nil
}

func hppV2DayWindow(date *time.Time) (time.Time, time.Time, error) {
	if date == nil {
		now := time.Now()
		date = &now
	}

	location, err := time.LoadLocation("Asia/Jakarta")
	if err != nil {
		return time.Time{}, time.Time{}, err
	}

	startOfDay := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location)
	endOfDay := startOfDay.Add(24 * time.Hour)
	return startOfDay, endOfDay, nil
}

func adjustmentRowTotalCost(row commonRepo.HppV2AdjustmentCostRow) float64 {
	if row.GrandTotal > 0 {
		return row.GrandTotal
	}
	return row.Qty * row.Price
}

func buildExpensePartFromRows(
	rows []commonRepo.HppV2ExpenseCostRow,
	code string,
	title string,
	scopes []string,
	proration *HppV2Proration,
	ratio float64,
) *HppV2ComponentPart {
	if len(rows) == 0 {
		return nil
	}

	total := 0.0
	references := make([]HppV2Reference, 0, len(rows))
	for _, row := range rows {
		total += row.TotalCost * ratio
		references = append(references, HppV2Reference{
			Type:         "expense_realization",
			ID:           row.ExpenseRealizationID,
			ProductID:    row.NonstockID,
			ProductName:  row.NonstockName,
			Date:         row.RealizationDate.Format("2006-01-02"),
			Qty:          row.Qty,
			UnitPrice:    row.Price,
			Total:        row.TotalCost,
			AppliedTotal: row.TotalCost * ratio,
		})
	}
	if total == 0 {
		return nil
	}

	return &HppV2ComponentPart{
		Code:       code,
		Title:      title,
		Scopes:     append([]string{}, scopes...),
		Total:      total,
		Proration:  proration,
		References: references,
	}
}

func buildChickinPartFromRows(
	rows []commonRepo.HppV2ChickinCostRow,
	code string,
	title string,
	scopes []string,
	proration *HppV2Proration,
	ratio float64,
) *HppV2ComponentPart {
	if len(rows) == 0 {
		return nil
	}

	total := 0.0
	references := make([]HppV2Reference, 0, len(rows))
	for _, row := range rows {
		total += row.TotalCost * ratio
		projectFlockKandangID := row.ProjectFlockKandangID
		references = append(references, HppV2Reference{
			Type:                  "project_chickin",
			ID:                    row.ProjectChickinID,
			StockableType:         row.StockableType,
			ProjectFlockKandangID: &projectFlockKandangID,
			ProductID:             row.SourceProductID,
			ProductName:           row.SourceProductName,
			Date:                  row.ChickInDate.Format("2006-01-02"),
			Qty:                   row.Qty,
			UnitPrice:             row.UnitPrice,
			Total:                 row.TotalCost,
			AppliedTotal:          row.TotalCost * ratio,
		})
	}
	if total == 0 {
		return nil
	}

	return &HppV2ComponentPart{
		Code:       code,
		Title:      title,
		Scopes:     append([]string{}, scopes...),
		Total:      total,
		Proration:  proration,
		References: references,
	}
}

func componentHasScope(component *HppV2Component, scope string) bool {
	if component == nil || scope == "" {
		return false
	}
	for _, candidate := range component.Scopes {
		if candidate == scope {
			return true
		}
	}
	return false
}

func componentScopeTotal(component *HppV2Component, scope string) float64 {
	if component == nil || scope == "" {
		return 0
	}

	total := 0.0
	hasPartScopes := false
	for _, part := range component.Parts {
		if len(part.Scopes) == 0 {
			continue
		}
		hasPartScopes = true
		if partHasScope(&part, scope) {
			total += part.Total
		}
	}
	if hasPartScopes {
		return total
	}
	if componentHasScope(component, scope) {
		return component.Total
	}
	return 0
}

func partHasScope(part *HppV2ComponentPart, scope string) bool {
	if part == nil || scope == "" {
		return false
	}
	for _, candidate := range part.Scopes {
		if candidate == scope {
			return true
		}
	}
	return false
}

func dateOnly(value time.Time) time.Time {
	return time.Date(value.Year(), value.Month(), value.Day(), 0, 0, 0, 0, value.Location())
}

func formatDateOnly(value time.Time) string {
	return dateOnly(value).Format("2006-01-02")
}
