package service

import (
	"context"
	"math"
	"time"

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

type HppService interface {
	CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error)
	GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error)
	GetTotalProductionCost(projectFlockKandangId uint, endDate *time.Time, depresiasiTransfer float64) (float64, error)
	GetBudgetKandangLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error)
	GetDepresiasiTransfer(projectFlockKandangId uint, date *time.Time) (float64, error)
	GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error)
}

type HppCostResponse struct {
	Estimation  HppCostDetail       `json:"estimation"`
	Real        HppCostDetail       `json:"real"`
	DebugValues *HppCostDebugValues `json:"debug_values,omitempty"`
}

type HppCostDetail struct {
	HargaKg    float64 `json:"harga_kg"`
	HargaButir float64 `json:"harga_butir"`
	Total      float64 `json:"total"`
	Kg         float64 `json:"kg"`
	Butir      float64 `json:"butir"`
}

type hppService struct {
	hppRepo commonRepo.HppCostRepository
}

func NewHppService(hppRepo commonRepo.HppCostRepository) HppService {
	return &hppService{hppRepo: hppRepo}
}

func (s *hppService) CalculateHppCost(projectFlockKandangId uint, date *time.Time) (*HppCostResponse, error) {
	if date == nil {
		now := time.Now()
		date = &now
	}

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

		return nil, err
	}

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

	depresiasiTransfer, err := s.GetDepresiasiTransfer(projectFlockKandangId, &endOfDay)
	if err != nil {

		return nil, err
	}

	totalProductionCost, err := s.GetTotalProductionCost(projectFlockKandangId, &endOfDay, depresiasiTransfer)
	if err != nil {

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

		return nil, err
	}
	return result, nil
}

func (s *hppService) GetTotalDepresiasiFlockGrowing(sourceProjectFlockID uint, date *time.Time) (float64, error) {
	if date == nil {
		now := time.Now()
		date = &now
	}

	if s.hppRepo == nil {

		return 0, nil
	}

	kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), sourceProjectFlockID)
	if err != nil {

		return 0, err
	}

	docCost, err := s.hppRepo.GetDocCost(context.Background(), kandangIDs)
	if err != nil {

		return 0, err
	}

	budgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), sourceProjectFlockID)
	if err != nil {

		return 0, err
	}

	expedisionCost, err := s.hppRepo.GetExpedisionCost(context.Background(), kandangIDs)
	if err != nil {

		return 0, err
	}

	feedCost, err := s.hppRepo.GetFeedUsageCost(context.Background(), kandangIDs, date)
	if err != nil {

		return 0, err
	}

	ovkCost, err := s.hppRepo.GetOvkUsageCost(context.Background(), kandangIDs, date)
	if err != nil {

		return 0, err
	}

	total := docCost + budgetCost + expedisionCost + feedCost + ovkCost
	return total, nil
}

func (s *hppService) GetTotalProductionCost(projectFlockKandangId uint, endDate *time.Time, depresiasiTransfer float64) (float64, error) {
	// if date == nil {
	// 	now := time.Now()
	// 	date = &now
	// }

	costPullet, err := s.hppRepo.GetPulletCost(context.Background(), projectFlockKandangId)
	if err != nil {

		return 0, err
	}

	costFeed, err := s.hppRepo.GetFeedUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {

		return 0, err
	}

	costOvk, err := s.hppRepo.GetOvkUsageCost(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {

		return 0, err
	}

	costExpedision, err := s.hppRepo.GetExpedisionCost(context.Background(), []uint{projectFlockKandangId})
	if err != nil {

		return 0, err
	}

	costBudget, err := s.GetBudgetKandangLaying(projectFlockKandangId, endDate)
	if err != nil {

		return 0, err
	}

	// fmt.Println(costBudget, costExpedision, costOvk, costFeed, costPullet, depresiasiTransfer)

	// depresiasiTransfer = 0

	total := depresiasiTransfer + costPullet + costFeed + costOvk + costExpedision + costBudget
	return total, nil
}

func (s *hppService) GetBudgetKandangLaying(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	// if date == nil {
	// 	now := time.Now()
	// 	date = &now
	// }

	if s.hppRepo == nil {

		return 0, nil
	}

	projectFlockId, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId)
	if err != nil {

		return 0, err
	}

	projectFlockKandangIds, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockId)
	if err != nil {

		return 0, err
	}

	eggProduksiPiecesFlock, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), projectFlockKandangIds, endDate)
	if err != nil {

		return 0, err
	}

	eggProduksiPiecesKandang, _, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {

		return 0, err
	}

	totalBudgetCost, err := s.hppRepo.GetBudgetCostByProjectFlockId(context.Background(), projectFlockId)
	if err != nil {

		return 0, err
	}

	if eggProduksiPiecesFlock == 0 {

		return 0, nil
	}

	result := (totalBudgetCost * eggProduksiPiecesKandang) / eggProduksiPiecesFlock
	return result, nil
}

func (s *hppService) GetDepresiasiTransfer(projectFlockKandangId uint, endDate *time.Time) (float64, error) {
	if endDate == nil {
		now := time.Now()
		endDate = &now
	}

	if s.hppRepo == nil {

		return 0, nil
	}

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

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

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

	totalDepresiasiFlockGrowing, err := s.GetTotalDepresiasiFlockGrowing(sourceProjectFlockID, endDate)
	if err != nil {
		return 0, err
	}

	result := (totalDepresiasiFlockGrowing * transferTotalQty) / totalPopulationFlockGrowing
	return result, nil
}

func (s *hppService) getManualDepresiasiTransferFallback(projectFlockKandangId uint) (float64, error) {
	projectFlockID, err := s.hppRepo.GetProjectFlockIDByProjectFlockKandangID(context.Background(), projectFlockKandangId)
	if err != nil {

		return 0, err
	}
	if projectFlockID == 0 {

		return 0, nil
	}

	manualCost, err := s.hppRepo.GetManualDepreciationCostByProjectFlockID(context.Background(), projectFlockID)
	if err != nil {

		return 0, err
	}
	if manualCost <= 0 {

		return 0, nil
	}

	kandangIDs, err := s.hppRepo.GetProjectFlockKandangIDs(context.Background(), projectFlockID)
	if err != nil {

		return 0, err
	}
	if len(kandangIDs) == 0 {

		return 0, nil
	}

	totalUsageQty, err := s.hppRepo.GetTotalPopulation(context.Background(), kandangIDs)
	if err != nil {

		return 0, err
	}
	if totalUsageQty <= 0 {

		return 0, nil
	}

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

		return 0, err
	}
	if kandangUsageQty <= 0 {

		return 0, nil
	}

	result := manualCost * (kandangUsageQty / totalUsageQty)
	return result, nil
}

func (s *hppService) GetHppEstimationDanRealisasi(totalProductionCost float64, projectFlockKandangId uint, startDate *time.Time, endDate *time.Time) (*HppCostResponse, error) {

	if s.hppRepo == nil {

		return &HppCostResponse{}, nil
	}

	estimPieces, estimWeightKg, err := s.hppRepo.GetEggProduksiPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, endDate)
	if err != nil {

		return nil, err
	}

	realPieces, realWeightKg, err := s.hppRepo.GetEggTerjualPiecesAndWeightKgByProjectFlockKandangIds(context.Background(), []uint{projectFlockKandangId}, startDate, endDate)
	if err != nil {

		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)
	}

	result := &HppCostResponse{
		Estimation: estimation,
		Real:       real,
	}
	return result, nil
}

func roundToTwoDecimals(value float64) float64 {
	result := math.Round(value*100) / 100
	return result
}

func formatTimePtr(value *time.Time) string {
	if value == nil {
		return "<nil>"
	}
	return value.Format(time.RFC3339)
}
