package dto

import (
	"strings"
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
	areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
	locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/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"
	warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
)

type PurchaseRelationDTO struct {
	Id         uint       `json:"id"`
	PrNumber   string     `json:"pr_number"`
	PoNumber   *string    `json:"po_number"`
	PoDate     *time.Time `json:"po_date"`
	CreditTerm int        `json:"credit_term"`
	Notes      *string    `json:"notes"`
}

type PurchaseListDTO struct {
	PurchaseRelationDTO
	Supplier       *supplierDTO.SupplierRelationDTO `json:"supplier"`
	DueDate        *time.Time                       `json:"due_date"`
	CreatedUser    *userDTO.UserRelationDTO         `json:"created_user"`
	RequesterName  string                           `json:"requester_name"`
	PoExpedition   []PoExpeditionDTO                `json:"po_expedition"`
	Products       []productDTO.ProductRelationDTO  `json:"products"`
	Location       *locationDTO.LocationRelationDTO `json:"location"`
	Area           *areaDTO.AreaRelationDTO         `json:"area"`
	CreatedAt      time.Time                        `json:"created_at"`
	UpdatedAt      time.Time                        `json:"updated_at"`
	LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
}

type PurchaseDetailDTO struct {
	PurchaseRelationDTO
	Supplier       *supplierDTO.SupplierRelationDTO `json:"supplier"`
	DueDate        *time.Time                       `json:"due_date"`
	Items          []PurchaseItemDTO                `json:"items"`
	CreatedUser    *userDTO.UserRelationDTO         `json:"created_user"`
	CreatedAt      time.Time                        `json:"created_at"`
	UpdatedAt      time.Time                        `json:"updated_at"`
	LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
}

type PurchaseItemDTO struct {
	Id                 uint                               `json:"id"`
	ProductID          uint                               `json:"product_id"`
	Product            *productDTO.ProductRelationDTO     `json:"product"`
	WarehouseID        uint                               `json:"warehouse_id"`
	Warehouse          *warehouseDTO.WarehouseRelationDTO `json:"warehouse"`
	ProductWarehouseID *uint                              `json:"product_warehouse_id"`
	SubQty             float64                            `json:"sub_qty"`
	TotalQty           float64                            `json:"total_qty"`
	TotalUsed          float64                            `json:"total_used"`
	Price              float64                            `json:"price"`
	TotalPrice         float64                            `json:"total_price"`
	ReceivedDate       *time.Time                         `json:"received_date"`
	TravelNumber       *string                            `json:"travel_number"`
	TravelDocumentPath *string                            `json:"travel_document_path"`
	VehicleNumber      *string                            `json:"vehicle_number"`
	TransportPerItem   *float64                           `json:"transport_per_item,omitempty"`
	ExpeditionVendor   *supplierDTO.SupplierRelationDTO   `json:"expedition_vendor,omitempty"`
	HasChickin         bool                               `json:"has_chickin"`
}

type PoExpeditionDTO struct {
	Id       uint64 `json:"id"`
	Refrence string `json:"refrence"`
}

func ToPurchaseRelationDTO(p *entity.Purchase) PurchaseRelationDTO {
	return PurchaseRelationDTO{
		Id:         p.Id,
		PrNumber:   p.PrNumber,
		PoNumber:   p.PoNumber,
		PoDate:     p.PoDate,
		CreditTerm: p.CreditTerm,
		Notes:      p.Notes,
	}
}

func ToPurchaseItemDTO(item entity.PurchaseItem) PurchaseItemDTO {
	dto := PurchaseItemDTO{
		Id:                 item.Id,
		ProductID:          item.ProductId,
		ProductWarehouseID: item.ProductWarehouseId,
		WarehouseID:        item.WarehouseId,
		SubQty:             item.SubQty,
		TotalQty:           item.TotalQty,
		TotalUsed:          item.TotalUsed,
		Price:              item.Price,
		TotalPrice:         item.TotalPrice,
		ReceivedDate:       item.ReceivedDate,
		TravelNumber:       item.TravelNumber,
		TravelDocumentPath: item.TravelNumberDocs,
		VehicleNumber:      item.VehicleNumber,
		HasChickin:         item.HasChickin,
	}

	if item.Product != nil && item.Product.Id != 0 {
		summary := productDTO.ToProductRelationDTO(*item.Product)
		dto.Product = &summary
	}

	if item.Warehouse != nil && item.Warehouse.Id != 0 {
		summary := warehouseDTO.ToWarehouseRelationDTO(*item.Warehouse)
		if item.Warehouse.Area.Id != 0 {
			areaSummary := areaDTO.ToAreaRelationDTO(item.Warehouse.Area)
			summary.Area = &areaSummary
		}
		if item.Warehouse.Location != nil && item.Warehouse.Location.Id != 0 {
			locationSummary := locationDTO.ToLocationRelationDTO(*item.Warehouse.Location)
			summary.Location = &locationSummary
		}
		dto.Warehouse = &summary
	}

	if item.ExpenseNonstock != nil {
		priceCopy := item.ExpenseNonstock.Price
		dto.TransportPerItem = &priceCopy

		if item.ExpenseNonstock.Expense != nil {
			exp := item.ExpenseNonstock.Expense

			if exp.Supplier != nil && exp.Supplier.Id != 0 {
				supplierSummary := supplierDTO.ToSupplierRelationDTO(*exp.Supplier)
				dto.ExpeditionVendor = &supplierSummary
			}
		}
	}

	return dto
}

func ToPurchaseItemDTOs(items []entity.PurchaseItem) []PurchaseItemDTO {
	result := make([]PurchaseItemDTO, len(items))
	for i, item := range items {
		result[i] = ToPurchaseItemDTO(item)
	}
	return result
}

func ToPurchaseListDTO(p entity.Purchase) PurchaseListDTO {
	var supplier *supplierDTO.SupplierRelationDTO
	if p.Supplier.Id != 0 {
		mapped := supplierDTO.ToSupplierRelationDTO(p.Supplier)
		supplier = &mapped
	}

	var createdUser *userDTO.UserRelationDTO
	if p.CreatedUser.Id != 0 {
		mapped := userDTO.ToUserRelationDTO(p.CreatedUser)
		createdUser = &mapped
	}
	requesterName := ""
	if createdUser != nil {
		requesterName = createdUser.Name
	}

	var latestApproval *approvalDTO.ApprovalRelationDTO
	if p.LatestApproval != nil && p.LatestApproval.Id != 0 {
		mapped := approvalDTO.ToApprovalDTO(*p.LatestApproval)
		latestApproval = &mapped
	}

	var (
		poExpedition = make([]PoExpeditionDTO, 0)
		location     *locationDTO.LocationRelationDTO
		area         *areaDTO.AreaRelationDTO
	)
	productMap := make(map[uint]productDTO.ProductRelationDTO)
	expeditionRefSet := make(map[uint64]struct{})
	for i := range p.Items {
		item := p.Items[i]
		if item.Product != nil && item.Product.Id != 0 {
			if _, exists := productMap[item.Product.Id]; !exists {
				productMap[item.Product.Id] = productDTO.ToProductRelationDTO(*item.Product)
			}
		}
		if item.ExpenseNonstock != nil && item.ExpenseNonstock.Expense != nil {
			exp := item.ExpenseNonstock.Expense
			ref := strings.TrimSpace(exp.ReferenceNumber)
			if exp.Id != 0 && ref != "" {
				if _, exists := expeditionRefSet[exp.Id]; !exists {
					expeditionRefSet[exp.Id] = struct{}{}
					poExpedition = append(poExpedition, PoExpeditionDTO{
						Id:       exp.Id,
						Refrence: ref,
					})
				}
			}
		}
		if location == nil && item.Warehouse != nil && item.Warehouse.Location != nil && item.Warehouse.Location.Id != 0 {
			loc := locationDTO.ToLocationRelationDTO(*item.Warehouse.Location)
			location = &loc
		}
		if area == nil && item.Warehouse != nil && item.Warehouse.Area.Id != 0 {
			ar := areaDTO.ToAreaRelationDTO(item.Warehouse.Area)
			area = &ar
		}
	}
	products := make([]productDTO.ProductRelationDTO, 0, len(productMap))
	for _, prod := range productMap {
		products = append(products, prod)
	}

	return PurchaseListDTO{
		PurchaseRelationDTO: ToPurchaseRelationDTO(&p),
		Supplier:            supplier,
		DueDate:             p.DueDate,
		CreatedUser:         createdUser,
		RequesterName:       requesterName,
		PoExpedition:        poExpedition,
		Products:            products,
		Location:            location,
		Area:                area,
		CreatedAt:           p.CreatedAt,
		UpdatedAt:           p.UpdatedAt,
		LatestApproval:      latestApproval,
	}
}

func ToPurchaseListDTOs(items []entity.Purchase) []PurchaseListDTO {
	if len(items) == 0 {
		return make([]PurchaseListDTO, 0)
	}
	result := make([]PurchaseListDTO, len(items))
	for i, item := range items {
		result[i] = ToPurchaseListDTO(item)
	}
	return result
}

func ToPurchaseDetailDTO(p entity.Purchase) PurchaseDetailDTO {
	var supplier *supplierDTO.SupplierRelationDTO
	if p.Supplier.Id != 0 {
		mapped := supplierDTO.ToSupplierRelationDTO(p.Supplier)
		supplier = &mapped
	}

	var createdUser *userDTO.UserRelationDTO
	if p.CreatedUser.Id != 0 {
		mapped := userDTO.ToUserRelationDTO(p.CreatedUser)
		createdUser = &mapped
	}

	var latestApproval *approvalDTO.ApprovalRelationDTO
	if p.LatestApproval != nil && p.LatestApproval.Id != 0 {
		mapped := approvalDTO.ToApprovalDTO(*p.LatestApproval)
		latestApproval = &mapped
	}

	return PurchaseDetailDTO{
		PurchaseRelationDTO: ToPurchaseRelationDTO(&p),
		Supplier:            supplier,
		DueDate:             p.DueDate,
		Items:               ToPurchaseItemDTOs(p.Items),
		CreatedUser:         createdUser,
		CreatedAt:           p.CreatedAt,
		UpdatedAt:           p.UpdatedAt,
		LatestApproval:      latestApproval,
	}
}
