package dto

import (
	"strconv"
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
	productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto"
	areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
	kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
	locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
	productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"
	productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
	warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
	chickinDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/dto"
	projectFlockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/dto"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
)

// === DTO Structs (ordered) ===

type ProjectFlockKandangRelationDTO struct {
	Id             uint `json:"id"`
	KandangId      uint `json:"kandang_id"`
	ProjectFlockId uint `json:"project_flock_id"`
}

type ProjectFlockDTO struct {
	projectFlockDTO.ProjectFlockRelationDTO
	Area               *areaDTO.AreaRelationDTO                             `json:"area,omitempty"`
	Category           string                                               `json:"category"`
	StandardFcr        *float64                                             `json:"standard_fcr,omitempty"`
	ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
	Location           *locationDTO.LocationRelationDTO                     `json:"location,omitempty"`
	CreatedUser        *userDTO.UserRelationDTO                             `json:"created_user,omitempty"`
	CreatedAt          time.Time                                            `json:"created_at"`
	UpdatedAt          time.Time                                            `json:"updated_at"`
}

type ProductWarehouseDTO struct {
	productWarehouseDTO.ProductWarehouseRelationDTO
	Product   *productDTO.ProductRelationDTO     `json:"product,omitempty"`
	Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
}

type AvailableQtyDTO struct {
	AvailableQty     float64              `json:"available_qty"`
	ProductWarehouse *ProductWarehouseDTO `json:"product_warehouse,omitempty"`
}

type ProjectFlockKandangListDTO struct {
	ProjectFlockKandangRelationDTO
	ProjectFlock    *ProjectFlockDTO                 `json:"project_flock,omitempty"`
	Kandang         *kandangDTO.KandangRelationDTO   `json:"kandang,omitempty"`
	NameWithPeriod  string                           `json:"name_with_period"`
	CreatedUser     *userDTO.UserRelationDTO         `json:"created_user,omitempty"`
	CreatedAt       time.Time                        `json:"created_at"`
	Approval        *approvalDTO.ApprovalRelationDTO `json:"approval,omitempty"`
	ChickinApproval *approvalDTO.ApprovalRelationDTO `json:"chickin_approval,omitempty"`
}

type ProjectFlockKandangNameWithPeriodDTO struct {
	Id             uint   `json:"id"`
	NameWithPeriod string `json:"name_with_period"`
}

type ProjectFlockKandangDetailDTO struct {
	ProjectFlockKandangListDTO
	Chickins      []chickinDTO.ChickinRelationDTO `json:"chickins,omitempty"`
	AvailableQtys []AvailableQtyDTO               `json:"available_qtys,omitempty"`
}

// === Mapper Functions (ordered) ===

func ToProjectFlockKandangRelationDTO(e entity.ProjectFlockKandang) ProjectFlockKandangRelationDTO {
	return ProjectFlockKandangRelationDTO{
		Id:             e.Id,
		KandangId:      e.KandangId,
		ProjectFlockId: e.ProjectFlockId,
	}
}

func toProjectFlockDTO(pf *projectFlockDTO.ProjectFlockListDTO) *ProjectFlockDTO {
	if pf == nil {
		return nil
	}

	return &ProjectFlockDTO{
		ProjectFlockRelationDTO: pf.ProjectFlockRelationDTO,
		Area:                    pf.Area,
		Category:                pf.Category,
		StandardFcr:             pf.StandardFcr,
		ProductionStandard:      pf.ProductionStandard,
		Location:                pf.Location,
		CreatedUser:             pf.CreatedUser,
		CreatedAt:               pf.CreatedAt,
		UpdatedAt:               pf.UpdatedAt,
	}
}

func ToProjectFlockKandangDetailDTOWithAvailableQty(e entity.ProjectFlockKandang, availableQtyMap map[uint]float64, productWarehouses []entity.ProductWarehouse) ProjectFlockKandangDetailDTO {
	var projectFlockSummary *projectFlockDTO.ProjectFlockListDTO
	if e.ProjectFlock.Id != 0 {
		mapped := projectFlockDTO.ToProjectFlockListDTO(e.ProjectFlock)
		projectFlockSummary = &mapped
	}

	listDTO := ProjectFlockKandangListDTO{
		ProjectFlockKandangRelationDTO: ToProjectFlockKandangRelationDTO(e),
		ProjectFlock:                   toProjectFlockDTO(projectFlockSummary),
		Kandang:                        toKandangRelation(e.Kandang),
		NameWithPeriod:                 toNameWithPeriod(e.Kandang, e.Period),
		CreatedAt:                      e.CreatedAt,
		CreatedUser:                    toCreatedUserDTO(e.ProjectFlock),
		Approval:                       toApprovalDTOSelector(e, func(x entity.ProjectFlockKandang) *entity.Approval { return x.LatestProjectFlockApproval }),
		ChickinApproval:                toApprovalDTOSelector(e, func(x entity.ProjectFlockKandang) *entity.Approval { return x.LatestChickinApproval }),
	}

	return ProjectFlockKandangDetailDTO{
		ProjectFlockKandangListDTO: listDTO,
		Chickins:                   toChickinDTOs(e.Chickins),
		AvailableQtys:              toAvailableQtyDTOsFromMap(e.Chickins, availableQtyMap, productWarehouses),
	}
}

func toKandangRelation(kandang entity.Kandang) *kandangDTO.KandangRelationDTO {
	if kandang.Id == 0 {
		return nil
	}

	mapped := kandangDTO.ToKandangRelationDTO(kandang)
	return &mapped
}

func toNameWithPeriod(kandang entity.Kandang, period int) string {
	return toNameWithPeriodValue(kandang.Name, period)
}

func toNameWithPeriodValue(kandangName string, period int) string {
	if kandangName == "" {
		return ""
	}
	if period == 0 {
		return kandangName
	}
	return kandangName + " Period " + strconv.Itoa(period)
}

func toApprovalDTOSelector(
	e entity.ProjectFlockKandang, selector func(entity.ProjectFlockKandang) *entity.Approval) *approvalDTO.ApprovalRelationDTO {
	approval := selector(e)
	if approval != nil {
		mapped := approvalDTO.ToApprovalDTO(*approval)
		return &mapped
	}
	return nil
}

func ToProjectFlockKandangListDTO(e entity.ProjectFlockKandang) ProjectFlockKandangListDTO {
	var projectFlockSummary *projectFlockDTO.ProjectFlockListDTO
	if e.ProjectFlock.Id != 0 {
		mapped := projectFlockDTO.ToProjectFlockListDTO(e.ProjectFlock)
		projectFlockSummary = &mapped
	}

	return ProjectFlockKandangListDTO{
		ProjectFlockKandangRelationDTO: ToProjectFlockKandangRelationDTO(e),
		ProjectFlock:                   toProjectFlockDTO(projectFlockSummary),
		Kandang:                        toKandangRelation(e.Kandang),
		NameWithPeriod:                 toNameWithPeriod(e.Kandang, e.Period),
		CreatedAt:                      e.CreatedAt,
		CreatedUser:                    toCreatedUserDTO(e.ProjectFlock),
		Approval:                       toApprovalDTOSelector(e, func(x entity.ProjectFlockKandang) *entity.Approval { return x.LatestProjectFlockApproval }),
		ChickinApproval:                toApprovalDTOSelector(e, func(x entity.ProjectFlockKandang) *entity.Approval { return x.LatestChickinApproval }),
	}
}

func ToProjectFlockKandangNameWithPeriodDTO(e entity.ProjectFlockKandang) ProjectFlockKandangNameWithPeriodDTO {
	return ProjectFlockKandangNameWithPeriodDTO{
		Id:             e.Id,
		NameWithPeriod: toNameWithPeriod(e.Kandang, e.Period),
	}
}

func ToProjectFlockKandangNameWithPeriodDTOValues(id uint, kandangName string, period int) ProjectFlockKandangNameWithPeriodDTO {
	return ProjectFlockKandangNameWithPeriodDTO{
		Id:             id,
		NameWithPeriod: toNameWithPeriodValue(kandangName, period),
	}
}

func toCreatedUserDTO(pf entity.ProjectFlock) *userDTO.UserRelationDTO {
	if pf.CreatedUser.Id != 0 {
		mapped := userDTO.ToUserRelationDTO(pf.CreatedUser)
		return &mapped
	} else if pf.CreatedBy != 0 {
		return &userDTO.UserRelationDTO{
			Id:     pf.CreatedBy,
			IdUser: int64(pf.CreatedBy),
		}
	}
	return nil
}

func toChickinDTOs(chickins []entity.ProjectChickin) []chickinDTO.ChickinRelationDTO {
	if len(chickins) == 0 {
		return nil
	}

	result := make([]chickinDTO.ChickinRelationDTO, len(chickins))
	for i, ch := range chickins {
		result[i] = chickinDTO.ToChickinRelationDTO(ch)
	}
	return result
}

func toAvailableQtyDTOsFromMap(chickins []entity.ProjectChickin, availableQtyMap map[uint]float64, productWarehouses []entity.ProductWarehouse) []AvailableQtyDTO {
	if len(availableQtyMap) == 0 {
		return nil
	}

	pwMap := make(map[uint]*entity.ProductWarehouse)
	for _, chickin := range chickins {
		if chickin.ProductWarehouse != nil && chickin.ProductWarehouse.Id != 0 {
			pwMap[chickin.ProductWarehouseId] = chickin.ProductWarehouse
		}
	}

	for i := range productWarehouses {
		if _, exists := pwMap[productWarehouses[i].Id]; !exists {
			pwMap[productWarehouses[i].Id] = &productWarehouses[i]
		}
	}

	result := make([]AvailableQtyDTO, 0, len(availableQtyMap))
	for pwId, availableQty := range availableQtyMap {

		if availableQty <= 0 {
			continue
		}

		pw, exists := pwMap[pwId]
		if !exists || pw == nil {
			continue
		}

		pwDTO := ToProductWarehouseDTO(pw)

		result = append(result, AvailableQtyDTO{
			AvailableQty:     availableQty,
			ProductWarehouse: pwDTO,
		})
	}

	return result
}

func ToProductWarehouseDTO(pw *entity.ProductWarehouse) *ProductWarehouseDTO {
	if pw == nil {
		return nil
	}

	base := productWarehouseDTO.ToProductWarehouseRelationDTO(*pw)

	var product *productDTO.ProductRelationDTO
	if pw.Product.Id != 0 {
		mapped := productDTO.ToProductRelationDTO(pw.Product)
		product = &mapped
	}

	var warehouse *warehouseDTO.WarehouseRelationDTO
	if pw.Warehouse.Id != 0 {
		mapped := warehouseDTO.ToWarehouseRelationDTO(pw.Warehouse)
		warehouse = &mapped
	}

	return &ProductWarehouseDTO{
		ProductWarehouseRelationDTO: base,
		Product:                     product,
		Warehouse:                   warehouse,
	}
}
