package dto

import (
	"encoding/json"
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	marketingDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/dto"
	customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
	productCategoryDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/dto"
	productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
	supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"
	uomDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/dto"
	warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
)

type RepportMarketingItemDTO struct {
	ID              int                                `json:"id"`
	SoDate          time.Time                          `json:"so_date"`
	RealizationDate time.Time                          `json:"realization_date"`
	AgingDays       int                                `json:"aging_days"`
	Warehouse       *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
	Customer        *customerDTO.CustomerRelationDTO   `json:"customer,omitempty"`
	DoNumber        string                             `json:"do_number"`
	Sales           *userDTO.UserRelationDTO           `json:"sales,omitempty"`
	VehicleNumber   string                             `json:"vehicle_number"`
	Product         *ProductRelationDTOFixed           `json:"product,omitempty"`
	MarketingType   string                             `json:"marketing_type"`
	Qty             float64                            `json:"qty"`
	AverageWeightKg float64                            `json:"average_weight_kg"`
	TotalWeightKg   float64                            `json:"total_weight_kg"`
	SalesPricePerKg float64                            `json:"sales_price_per_kg"`
	HppPricePerKg   float64                            `json:"hpp_price_per_kg"`
	SalesAmount     float64                            `json:"sales_amount"`
	HppAmount       float64                            `json:"hpp_amount"`
}

type Summary struct {
	TotalQty           int     `json:"total_qty"`
	TotalWeightKg      float64 `json:"total_weight_kg"`
	AverageWeightKg    float64 `json:"average_weight_kg"`
	AverageSalesPrice  float64 `json:"average_sales_price"`
	TotalSalesAmount   int64   `json:"total_sales_amount"`
	TotalHppAmount     int64   `json:"total_hpp_amount"`
	TotalHppPricePerKg float64 `json:"total_hpp_price_per_kg"`
}

type ProductRelationDTOFixed struct {
	productDTO.ProductRelationDTO
	ProductPrice float64  `json:"product_price"`
	SellingPrice *float64 `json:"selling_price,omitempty"`
}

func ToMarketingReportItems(mdps []entity.MarketingDeliveryProduct, hppByDelivery map[uint]float64, categoryByDelivery map[uint]string, agingMap map[int]int) []RepportMarketingItemDTO {
	items := make([]RepportMarketingItemDTO, 0, len(mdps))

	for _, mdp := range mdps {
		hppPerKg := hppByDelivery[mdp.Id]
		category := categoryByDelivery[mdp.Id]
		if category == "" {
			if projectFlockKandang := mdp.AttributedProjectFlockKandang; projectFlockKandang != nil {
				category = projectFlockKandang.ProjectFlock.Category
			} else if projectFlockKandang := mdp.MarketingProduct.ProductWarehouse.ProjectFlockKandang; projectFlockKandang != nil {
				category = projectFlockKandang.ProjectFlock.Category
			}
		}

		soDate := time.Time{}
		agingDays := 0
		if mdp.MarketingProduct.Marketing.SoDate.Year() > 1 {
			soDate = mdp.MarketingProduct.Marketing.SoDate
			if ag, exists := agingMap[int(mdp.Id)]; exists {
				agingDays = ag
			} else {
				agingDays = int(time.Since(soDate).Hours() / 24)
			}
		}

		realizationDate := time.Time{}
		if mdp.DeliveryDate != nil {
			realizationDate = *mdp.DeliveryDate
		}

		totalWeightKg := mdp.UsageQty * mdp.AvgWeight
		salesAmount := totalWeightKg * mdp.UnitPrice

		var hpp float64
		var hppAmount float64

		var hasAyam, hasTelur, hasTrading bool
		for _, flag := range mdp.MarketingProduct.ProductWarehouse.Product.Flags {
			ft := utils.FlagType(flag.Name)

			if ft == utils.FlagAyamAfkir || ft == utils.FlagAyamCulling || ft == utils.FlagAyamMati ||
				ft == utils.FlagDOC || ft == utils.FlagPullet || ft == utils.FlagLayer {
				hasAyam = true
			}

			if ft == utils.FlagTelur || ft == utils.FlagTelurUtuh || ft == utils.FlagTelurPecah ||
				ft == utils.FlagTelurPutih || ft == utils.FlagTelurRetak {
				hasTelur = true
			}

			if ft == utils.FlagOVK || ft == utils.FlagObat || ft == utils.FlagVitamin || ft == utils.FlagKimia ||
				ft == utils.FlagPakan || ft == utils.FlagPreStarter || ft == utils.FlagStarter || ft == utils.FlagFinisher {
				hasTrading = true
			}
		}

		marketingType := "trading"
		if hasTrading {
			marketingType = "trading"
		} else if hasTelur {
			marketingType = "telur"
		} else if hasAyam {
			marketingType = "ayam"
		}

		eligibleForHpp := false

		if utils.ProjectFlockCategory(category) == utils.ProjectFlockCategoryGrowing {
			eligibleForHpp = hasAyam
		} else {
			eligibleForHpp = hasAyam || hasTelur
		}

		if eligibleForHpp {
			hpp = hppPerKg
			hppAmount = totalWeightKg * hppPerKg
		}

		item := RepportMarketingItemDTO{
			ID:              int(mdp.Id),
			SoDate:          soDate,
			RealizationDate: realizationDate,
			AgingDays:       agingDays,
			DoNumber:        marketingDTO.GenerateDeliveryOrderNumber(mdp.MarketingProduct.Marketing.SoNumber, mdp.DeliveryDate, mdp.MarketingProduct.ProductWarehouse.WarehouseId),
			MarketingType:   marketingType,
			Qty:             mdp.UsageQty,
			AverageWeightKg: mdp.AvgWeight,
			TotalWeightKg:   totalWeightKg,
			SalesPricePerKg: mdp.UnitPrice,
			HppPricePerKg:   hpp,
			SalesAmount:     salesAmount,
			HppAmount:       hppAmount,
			VehicleNumber:   mdp.VehicleNumber,
		}

		if mdp.MarketingProduct.ProductWarehouse.WarehouseId != 0 {
			mapped := warehouseDTO.ToWarehouseRelationDTO(mdp.MarketingProduct.ProductWarehouse.Warehouse)
			item.Warehouse = &mapped
		}

		if mdp.MarketingProduct.Marketing.CustomerId != 0 {
			mapped := customerDTO.ToCustomerRelationDTO(mdp.MarketingProduct.Marketing.Customer)
			item.Customer = &mapped
		}

		if mdp.MarketingProduct.Marketing.SalesPersonId != 0 {
			mapped := userDTO.ToUserRelationDTO(mdp.MarketingProduct.Marketing.SalesPerson)
			item.Sales = &mapped
		}

		if mdp.MarketingProduct.ProductWarehouse.ProductId != 0 {
			mapped := productDTO.ToProductRelationDTO(mdp.MarketingProduct.ProductWarehouse.Product)
			item.Product = newProductRelationDTOFixedPtr(&mapped)
		}

		items = append(items, item)
	}

	return items
}

func ToSummaryFromDTOItems(items []RepportMarketingItemDTO) *Summary {
	if len(items) == 0 {
		return nil
	}

	totalQty := 0
	totalWeightKg := 0.0
	avgSalesPrice := 0.0
	avgWeightKg := 0.0
	totalSalesAmount := int64(0)
	totalHppAmount := int64(0)

	for _, item := range items {
		totalQty += int(item.Qty)
		totalWeightKg += item.TotalWeightKg
		totalSalesAmount += int64(item.SalesAmount)
		totalHppAmount += int64(item.HppAmount)
	}

	totalHppPricePerKg := float64(0)

	if totalWeightKg > 0 {
		totalHppPricePerKg = float64(totalHppAmount) / totalWeightKg
		avgSalesPrice = float64(totalSalesAmount) / totalWeightKg
	}

	if totalQty > 0 {
		avgWeightKg = totalWeightKg / float64(totalQty)
	}

	return &Summary{
		TotalQty:           totalQty,
		TotalWeightKg:      totalWeightKg,
		AverageWeightKg:    avgWeightKg,
		AverageSalesPrice:  avgSalesPrice,
		TotalSalesAmount:   totalSalesAmount,
		TotalHppAmount:     totalHppAmount,
		TotalHppPricePerKg: totalHppPricePerKg,
	}
}

func newProductRelationDTOFixedPtr(original *productDTO.ProductRelationDTO) *ProductRelationDTOFixed {
	if original == nil {
		return nil
	}
	fixed := ProductRelationDTOFixed{
		ProductRelationDTO: *original,
		ProductPrice:       original.ProductPrice,
		SellingPrice:       original.SellingPrice,
	}
	return &fixed
}

func (p ProductRelationDTOFixed) MarshalJSON() ([]byte, error) {
	type Alias struct {
		Id              uint                                           `json:"id"`
		Name            string                                         `json:"name"`
		ProductPrice    float64                                        `json:"product_price"`
		SellingPrice    *float64                                       `json:"selling_price"`
		Uom             *uomDTO.UomRelationDTO                         `json:"uom,omitempty"`
		Flags           *[]string                                      `json:"flags,omitempty"`
		ProductCategory *productCategoryDTO.ProductCategoryRelationDTO `json:"product_category,omitempty"`
		Suppliers       []supplierDTO.SupplierRelationDTO              `json:"suppliers"`
	}

	suppliers := make([]supplierDTO.SupplierRelationDTO, len(p.ProductRelationDTO.Suppliers))
	for i, ps := range p.ProductRelationDTO.Suppliers {
		suppliers[i] = supplierDTO.SupplierRelationDTO{
			Id:       ps.Id,
			Name:     ps.Name,
			Alias:    ps.Alias,
			Category: ps.Category,
		}
	}

	return json.Marshal(&Alias{
		Id:              p.ProductRelationDTO.Id,
		Name:            p.ProductRelationDTO.Name,
		ProductPrice:    p.ProductPrice,
		SellingPrice:    p.SellingPrice,
		Uom:             p.ProductRelationDTO.Uom,
		Flags:           p.ProductRelationDTO.Flags,
		ProductCategory: p.ProductRelationDTO.ProductCategory,
		Suppliers:       suppliers,
	})
}
