package dto

import (
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	productCategoryDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/product-categories/dto"
	uomDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/uoms/dto"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
	utils "gitlab.com/mbugroup/lti-api.git/internal/utils"
)

// === DTO Structs ===

type ProductRelationDTO struct {
	Id              uint                                           `json:"id"`
	Name            string                                         `json:"name"`
	ProductPrice    float64                                        `gorm:"type:numeric(15,3);not null"`
	SellingPrice    *float64                                       `gorm:"type:numeric(15,3)"`
	Uom             *uomDTO.UomRelationDTO                         `json:"uom,omitempty"`
	Flag            *string                                        `json:"flag,omitempty"`
	SubFlag         *string                                        `json:"sub_flag,omitempty"`
	SubFlags        *[]string                                      `json:"sub_flags,omitempty"`
	Flags           *[]string                                      `json:"flags,omitempty"`
	ProductCategory *productCategoryDTO.ProductCategoryRelationDTO `json:"product_category,omitempty"`
	Suppliers       []ProductSupplierDTO                           `json:"suppliers"`
}

type ProductListDTO struct {
	Id              uint                                           `json:"id"`
	Name            string                                         `json:"name"`
	Brand           string                                         `json:"brand"`
	Sku             *string                                        `json:"sku,omitempty"`
	ProductPrice    float64                                        `json:"product_price"`
	SellingPrice    *float64                                       `json:"selling_price,omitempty"`
	Tax             *float64                                       `json:"tax,omitempty"`
	ExpiryPeriod    *int                                           `json:"expiry_period,omitempty"`
	Flag            *string                                        `json:"flag,omitempty"`
	SubFlag         *string                                        `json:"sub_flag,omitempty"`
	SubFlags        []string                                       `json:"sub_flags,omitempty"`
	Flags           []string                                       `json:"flags"`
	Uom             *uomDTO.UomRelationDTO                         `json:"uom,omitempty"`
	ProductCategory *productCategoryDTO.ProductCategoryRelationDTO `json:"product_category,omitempty"`
	Suppliers       []ProductSupplierDTO                           `json:"suppliers"`
	CreatedUser     *userDTO.UserRelationDTO                       `json:"created_user"`
	CreatedAt       time.Time                                      `json:"created_at"`
	UpdatedAt       time.Time                                      `json:"updated_at"`
}

type ProductDetailDTO struct {
	ProductListDTO
}

type ProductSupplierDTO struct {
	Id       uint    `json:"id"`
	Name     string  `json:"name"`
	Alias    string  `json:"alias"`
	Category string  `json:"category"`
	Price    float64 `json:"price"`
}

// === Mapper Functions ===

func ToProductRelationDTO(e entity.Product) ProductRelationDTO {
	flags := make([]string, len(e.Flags))
	for i, f := range e.Flags {
		flags[i] = f.Name
	}
	flag, subFlag, subFlags := resolveProductFlagAndSubFlags(flags)
	var subFlagsRef *[]string
	if len(subFlags) > 0 {
		values := make([]string, len(subFlags))
		copy(values, subFlags)
		subFlagsRef = &values
	}

	var uomRef *uomDTO.UomRelationDTO
	if e.Uom.Id != 0 {
		mapped := uomDTO.ToUomRelationDTO(e.Uom)
		uomRef = &mapped
	}

	var categoryRef *productCategoryDTO.ProductCategoryRelationDTO
	if e.ProductCategory.Id != 0 {
		mapped := productCategoryDTO.ToProductCategoryRelationDTO(e.ProductCategory)
		categoryRef = &mapped
	}

	return ProductRelationDTO{
		Id:              e.Id,
		Name:            e.Name,
		ProductPrice:    e.ProductPrice,
		SellingPrice:    e.SellingPrice,
		Flag:            flag,
		SubFlag:         subFlag,
		SubFlags:        subFlagsRef,
		Flags:           &flags,
		Uom:             uomRef,
		ProductCategory: categoryRef,
		Suppliers:       toProductSupplierDTOs(e.ProductSuppliers),
	}
}

func ToProductListDTO(e entity.Product) ProductListDTO {
	var createdUser *userDTO.UserRelationDTO
	if e.CreatedUser.Id != 0 {
		mapped := userDTO.ToUserRelationDTO(e.CreatedUser)
		createdUser = &mapped
	}

	var categoryRef *productCategoryDTO.ProductCategoryRelationDTO
	if e.ProductCategory.Id != 0 {
		mapped := productCategoryDTO.ToProductCategoryRelationDTO(e.ProductCategory)
		categoryRef = &mapped
	}

	flags := make([]string, len(e.Flags))
	for i, f := range e.Flags {
		flags[i] = f.Name
	}
	flag, subFlag, subFlags := resolveProductFlagAndSubFlags(flags)

	var uomRef *uomDTO.UomRelationDTO
	if e.Uom.Id != 0 {
		mapped := uomDTO.ToUomRelationDTO(e.Uom)
		uomRef = &mapped
	}

	return ProductListDTO{
		Id:              e.Id,
		Name:            e.Name,
		Flag:            flag,
		SubFlag:         subFlag,
		SubFlags:        subFlags,
		Flags:           flags,
		Uom:             uomRef,
		Brand:           e.Brand,
		Sku:             e.Sku,
		ProductPrice:    e.ProductPrice,
		SellingPrice:    e.SellingPrice,
		Tax:             e.Tax,
		ExpiryPeriod:    e.ExpiryPeriod,
		CreatedAt:       e.CreatedAt,
		UpdatedAt:       e.UpdatedAt,
		CreatedUser:     createdUser,
		ProductCategory: categoryRef,
		Suppliers:       toProductSupplierDTOs(e.ProductSuppliers),
	}
}

func ToProductListDTOs(e []entity.Product) []ProductListDTO {
	result := make([]ProductListDTO, len(e))
	for i, r := range e {
		result[i] = ToProductListDTO(r)
	}
	return result
}

func ToProductDetailDTO(e entity.Product) ProductDetailDTO {
	return ProductDetailDTO{
		ProductListDTO: ToProductListDTO(e),
	}
}

func resolveProductFlagAndSubFlags(flags []string) (*string, *string, []string) {
	normalized := utils.NormalizeFlagTypes(flags)
	if len(normalized) == 0 {
		return nil, nil, nil
	}

	available := make(map[utils.FlagType]struct{}, len(normalized))
	for _, flag := range normalized {
		available[flag] = struct{}{}
	}

	var selectedFlag utils.FlagType
	for _, mainFlag := range utils.ProductMainFlags() {
		if _, ok := available[mainFlag]; ok {
			selectedFlag = mainFlag
			break
		}
	}

	if selectedFlag == "" {
		subToMain := utils.ProductSubFlagToFlag()
		for _, flag := range normalized {
			if parent, ok := subToMain[flag]; ok {
				selectedFlag = parent
				break
			}
		}
	}

	if selectedFlag == "" {
		return nil, nil, nil
	}

	flag := string(selectedFlag)

	var subFlag *string
	subFlagValues := make([]string, 0)
	subFlagsByMain := utils.ProductSubFlagsByFlag()
	for _, sub := range subFlagsByMain[selectedFlag] {
		if _, ok := available[sub]; ok {
			subFlagValues = append(subFlagValues, string(sub))
		}
	}

	if len(subFlagValues) > 0 {
		first := subFlagValues[0]
		subFlag = &first
	}

	return &flag, subFlag, subFlagValues
}

func toProductSupplierDTOs(relations []entity.ProductSupplier) []ProductSupplierDTO {
	if len(relations) == 0 {
		return make([]ProductSupplierDTO, 0)
	}

	result := make([]ProductSupplierDTO, 0, len(relations))
	for _, relation := range relations {
		if relation.Supplier.Id == 0 {
			continue
		}
		result = append(result, ProductSupplierDTO{
			Id:       relation.Supplier.Id,
			Name:     relation.Supplier.Name,
			Alias:    relation.Supplier.Alias,
			Category: relation.Supplier.Category,
			Price:    relation.Price,
		})
	}

	if len(result) == 0 {
		return make([]ProductSupplierDTO, 0)
	}

	return result
}
