package dto

import (
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
)

// === DTO Structs ===

type OverheadDTO struct {
	ItemName          string  `json:"item_name"`
	UOMName           string  `json:"uom_name"`
	BudgetQuantity    float64 `json:"budget_quantity"`
	BudgetUnitPrice   float64 `json:"budget_unit_price"`
	BudgetTotalAmount float64 `json:"budget_total_amount"`
	ActualDate        string  `json:"actual_date"`
	ActualQuantity    float64 `json:"actual_quantity"`
	ActualUnitPrice   float64 `json:"actual_unit_price"`
	ActualTotalAmount float64 `json:"actual_total_amount"`
	CostPerBird       float64 `json:"cost_per_bird"`
}

type TotalDTO struct {
	BudgetQuantity    float64 `json:"budget_quantity"`
	BudgetTotalAmount float64 `json:"budget_total_amount"`
	ActualQuantity    float64 `json:"actual_quantity"`
	ActualTotalAmount float64 `json:"actual_total_amount"`
	CostPerBird       float64 `json:"cost_per_bird"`
}

type OverheadListDTO struct {
	Total     TotalDTO      `json:"total"`
	Overheads []OverheadDTO `json:"overheads"`
}

// === Mapper Functions ===

func ToOverheadDTO(budget *entity.ProjectBudget, realization *entity.ExpenseRealization) OverheadDTO {
	if budget == nil && realization == nil {
		return OverheadDTO{}
	}

	var itemName, itemUOM string
	if budget != nil {
		itemName, itemUOM = getItemInfo(budget.Nonstock)
	}

	if itemName == "" && realization != nil && realization.ExpenseNonstock != nil {
		itemName, itemUOM = getItemInfo(realization.ExpenseNonstock.Nonstock)
	}

	dto := OverheadDTO{
		ItemName: itemName,
		UOMName:  itemUOM,
	}

	if budget != nil {
		dto.BudgetQuantity = budget.Qty
		dto.BudgetUnitPrice = budget.Price
		dto.BudgetTotalAmount = calculateTotal(budget.Qty, budget.Price)
	}

	if realization != nil {
		dto.ActualQuantity = realization.Qty
		dto.ActualUnitPrice = realization.Price
		dto.ActualTotalAmount = calculateTotal(realization.Qty, realization.Price)
		dto.ActualDate = formatRealizationDate(realization)
	}

	return dto
}

func ToOverheadListDTOs(budgets []entity.ProjectBudget, realizations []entity.ExpenseRealization, totalChickinQty, totalActualPopulation float64, isPerKandang bool, totalKandangCount int) OverheadListDTO {
	overheadsByNonstockID := make(map[uint]*OverheadDTO)
	latestDateByNonstockID := make(map[uint]string)

	for i := range budgets {
		nonstockID := budgets[i].NonstockId
		if overheadsByNonstockID[nonstockID] == nil {
			overheadsByNonstockID[nonstockID] = &OverheadDTO{}
		}

		itemName, itemUOM := getItemInfo(budgets[i].Nonstock)
		overheadsByNonstockID[nonstockID].ItemName = itemName
		overheadsByNonstockID[nonstockID].UOMName = itemUOM

		budgetQty := budgets[i].Qty
		budgetPrice := budgets[i].Price
		budgetTotal := calculateTotal(budgets[i].Qty, budgets[i].Price)

		// Budget division: per kandang view only
		if isPerKandang && totalKandangCount > 0 {
			budgetQty = budgetQty / float64(totalKandangCount)
			budgetTotal = budgetTotal / float64(totalKandangCount)
		}

		overheadsByNonstockID[nonstockID].BudgetQuantity = budgetQty
		overheadsByNonstockID[nonstockID].BudgetUnitPrice = budgetPrice
		overheadsByNonstockID[nonstockID].BudgetTotalAmount = budgetTotal
	}

	for i := range realizations {
		if realizations[i].ExpenseNonstock == nil || realizations[i].ExpenseNonstock.NonstockId == nil {
			continue
		}

		nonstockID := uint(*realizations[i].ExpenseNonstock.NonstockId)
		if overheadsByNonstockID[nonstockID] == nil {
			overheadsByNonstockID[nonstockID] = &OverheadDTO{}
		}

		qty := realizations[i].Qty
		totalAmount := calculateTotal(realizations[i].Qty, realizations[i].Price)

		overheadsByNonstockID[nonstockID].ActualQuantity += qty
		overheadsByNonstockID[nonstockID].ActualTotalAmount += totalAmount

		if overheadsByNonstockID[nonstockID].ItemName == "" {
			itemName, itemUOM := getItemInfo(realizations[i].ExpenseNonstock.Nonstock)
			overheadsByNonstockID[nonstockID].ItemName = itemName
			overheadsByNonstockID[nonstockID].UOMName = itemUOM
		}

		realizationDateStr := formatRealizationDate(&realizations[i])
		if realizationDateStr != "" {
			if latestDateByNonstockID[nonstockID] == "" || realizationDateStr > latestDateByNonstockID[nonstockID] {
				latestDateByNonstockID[nonstockID] = realizationDateStr
			}
		}
	}

	var totalBudgetQuantity, totalBudgetAmount, totalActualQuantity, totalActualAmount float64
	overheadItems := make([]OverheadDTO, 0, len(overheadsByNonstockID))

	for nonstockID, overhead := range overheadsByNonstockID {
		overhead.ActualDate = latestDateByNonstockID[nonstockID]

		overhead.CostPerBird = calculateCostPerBird(overhead.ActualTotalAmount, totalActualPopulation)

		if overhead.ActualQuantity > 0 {
			overhead.ActualUnitPrice = overhead.ActualTotalAmount / overhead.ActualQuantity
		}

		totalBudgetQuantity += overhead.BudgetQuantity
		totalBudgetAmount += overhead.BudgetTotalAmount
		totalActualQuantity += overhead.ActualQuantity
		totalActualAmount += overhead.ActualTotalAmount

		overheadItems = append(overheadItems, *overhead)
	}

	return OverheadListDTO{
		Total: TotalDTO{
			BudgetQuantity:    totalBudgetQuantity,
			BudgetTotalAmount: totalBudgetAmount,
			ActualQuantity:    totalActualQuantity,
			ActualTotalAmount: totalActualAmount,
			CostPerBird:       calculateCostPerBird(totalActualAmount, totalActualPopulation),
		},
		Overheads: overheadItems,
	}
}

func getItemInfo(nonstock *entity.Nonstock) (string, string) {
	if nonstock != nil && nonstock.Id != 0 {
		return nonstock.Name, nonstock.Uom.Name
	}
	return "", ""
}

func calculateTotal(qty, price float64) float64 {
	return qty * price
}

func calculateCostPerBird(totalPrice, totalActualPopulation float64) float64 {
	if totalActualPopulation > 0 {
		return totalPrice / totalActualPopulation
	}
	return 0
}

func formatRealizationDate(realization *entity.ExpenseRealization) string {
	if realization != nil && realization.ExpenseNonstock != nil && realization.ExpenseNonstock.Expense != nil {
		if !realization.ExpenseNonstock.Expense.RealizationDate.IsZero() {
			return realization.ExpenseNonstock.Expense.RealizationDate.Format("2006-01-02T15:04:05Z07:00")
		}
	}
	return ""
}
