package dto

import (
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
	utypes "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/types"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/uniformities/validations"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"

	"github.com/gofiber/fiber/v2"
)

type UniformitySamplingDTO struct {
	ChickQtyOfWeight float64 `json:"chick_qty_of_weight"`
	MeanWeight       float64 `json:"mean_weight"`
	MeanDown         float64 `json:"mean_down"`
	MeanUp           float64 `json:"mean_up"`
}

type UniformityResultDTO struct {
	UniformQty float64 `json:"uniform_qty"`
	OutsideQty float64 `json:"outside_qty"`
	Uniformity float64 `json:"uniformity"`
	Cv         float64 `json:"cv"`
}

type UniformityStandardDTO struct {
	MeanWeight *float64 `json:"mean_weight"`
	Uniformity *float64 `json:"uniformity"`
}

type UniformityDetailItemDTO struct {
	Id     int     `json:"id"`
	Weight float64 `json:"weight"`
	Range  string  `json:"range"`
}

type UniformityVerificationDTO struct {
	Sampling          UniformitySamplingDTO     `json:"sampling"`
	Result            UniformityResultDTO       `json:"result"`
	UniformityDetails []UniformityDetailItemDTO `json:"uniformity_details"`
}

type UniformityInfoDTO struct {
	Tanggal      string `json:"tanggal"`
	LokasiFarm   string `json:"lokasi_farm"`
	ProjectFlock string `json:"project_flock"`
	Kandang      string `json:"kandang"`
	FileName     string `json:"file_name"`
	FileURL      string `json:"file_url"`
}

type UniformityDetailDTO struct {
	Id                uint                             `json:"id"`
	InfoUmum          UniformityInfoDTO                `json:"info_umum"`
	Sampling          UniformitySamplingDTO            `json:"sampling"`
	Result            UniformityResultDTO              `json:"result"`
	Standard          *UniformityStandardDTO           `json:"standard"`
	LatestApproval    *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
	UniformityDetails []UniformityDetailItemDTO        `json:"uniformity_details"`
}

type UniformityListDTO struct {
	Id                    uint                             `json:"id"`
	ProjectFlockKandangId uint                             `json:"project_flock_kandang_id"`
	LocationName          string                           `json:"location_name"`
	FlockName             string                           `json:"flock_name"`
	KandangName           string                           `json:"kandang_name"`
	FileName              string                           `json:"file_name"`
	AppliedAt             *time.Time                       `json:"applied_at"`
	Week                  int                              `json:"week"`
	Status                string                           `json:"status"`
	Uniformity            float64                          `json:"uniformity"`
	Cv                    float64                          `json:"cv"`
	ChickQtyOfWeight      float64                          `json:"chick_qty_of_weight"`
	UniformQty            float64                          `json:"uniform_qty"`
	MeanUp                float64                          `json:"mean_up"`
	MeanDown              float64                          `json:"mean_down"`
	StandardMeanWeight    *float64                         `json:"standard_mean_weight"`
	StandardUniformity    *float64                         `json:"standard_uniformity"`
	ChartData             *utypes.UniformityChartData      `json:"chart_data,omitempty"`
	CreatedBy             uint                             `json:"created_by"`
	LatestApproval        *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
}

func NewDocumentForResponse(name string) *entity.Document {
	if name == "" {
		return nil
	}
	return &entity.Document{Name: name}
}

func ToUniformityVerificationDTO(calc utypes.UniformityCalculation) UniformityVerificationDTO {
	return UniformityVerificationDTO{
		Sampling:          toUniformitySamplingDTO(calc),
		Result:            toUniformityResultDTO(calc),
		UniformityDetails: toUniformityDetailItemsDTO(calc),
	}
}

func ToUniformityDetailDTO(
	entityData entity.ProjectFlockKandangUniformity,
	calc utypes.UniformityCalculation,
	document *entity.Document,
	documentURL string,
	standard *UniformityStandardDTO,
) UniformityDetailDTO {
	info := UniformityInfoDTO{
		Tanggal:      formatUniformityDate(entityData.UniformDate),
		LokasiFarm:   resolveLocationName(entityData.ProjectFlockKandang),
		ProjectFlock: resolveProjectFlockName(entityData.ProjectFlockKandang),
		Kandang:      resolveKandangName(entityData.ProjectFlockKandang),
		FileName:     "",
		FileURL:      "",
	}
	if document != nil {
		info.FileName = document.Name
	}
	if documentURL != "" {
		info.FileURL = documentURL
	}

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

	return UniformityDetailDTO{
		Id:                entityData.Id,
		InfoUmum:          info,
		Sampling:          toUniformitySamplingDTO(calc),
		Result:            toUniformityResultDTO(calc),
		Standard:          standard,
		LatestApproval:    latestApproval,
		UniformityDetails: toUniformityDetailItemsDTO(calc),
	}
}

func ToUniformityListDTOs(items []entity.ProjectFlockKandangUniformity) []UniformityListDTO {
	result := make([]UniformityListDTO, len(items))
	for i, item := range items {
		var latestApproval *approvalDTO.ApprovalRelationDTO
		status := "Pengajuan"
		if item.LatestApproval != nil {
			mapped := approvalDTO.ToApprovalDTO(*item.LatestApproval)
			latestApproval = &mapped
			if mapped.StepName != "" {
				status = mapped.StepName
			}
		}

		result[i] = UniformityListDTO{
			Id:                    item.Id,
			ProjectFlockKandangId: item.ProjectFlockKandangId,
			LocationName:          resolveLocationName(item.ProjectFlockKandang),
			FlockName:             resolveProjectFlockName(item.ProjectFlockKandang),
			KandangName:           resolveKandangName(item.ProjectFlockKandang),
			AppliedAt:             item.UniformDate,
			Week:                  item.Week,
			Status:                status,
			Uniformity:            item.Uniformity,
			Cv:                    item.Cv,
			ChickQtyOfWeight:      item.ChickQtyOfWeight,
			UniformQty:            item.UniformQty,
			MeanUp:                item.MeanUp,
			MeanDown:              item.MeanDown,
			CreatedBy:             item.CreatedBy,
			LatestApproval:        latestApproval,
		}
	}
	return result
}

func ToUniformityListDTOsWithStandard(
	items []entity.ProjectFlockKandangUniformity,
	standards map[uint]utypes.UniformityStandard,
	documentNames map[uint]string,
	charts map[uint]utypes.UniformityChartData,
) []UniformityListDTO {
	result := ToUniformityListDTOs(items)
	if len(result) == 0 || len(standards) == 0 {
		for i := range result {
			if name, ok := documentNames[result[i].Id]; ok {
				result[i].FileName = name
			}
			if chart, ok := charts[result[i].Id]; ok {
				chartCopy := chart
				result[i].ChartData = &chartCopy
			}
		}
		return result
	}

	for i := range result {
		if std, ok := standards[result[i].Id]; ok {
			result[i].StandardMeanWeight = std.MeanWeight
			result[i].StandardUniformity = std.Uniformity
		}
		if name, ok := documentNames[result[i].Id]; ok {
			result[i].FileName = name
		}
		if chart, ok := charts[result[i].Id]; ok {
			chartCopy := chart
			result[i].ChartData = &chartCopy
		}
	}
	return result
}

func toUniformitySamplingDTO(calc utypes.UniformityCalculation) UniformitySamplingDTO {
	return UniformitySamplingDTO{
		ChickQtyOfWeight: calc.ChickQtyOfWeight,
		MeanWeight:       calc.MeanWeight,
		MeanDown:         calc.MeanDown,
		MeanUp:           calc.MeanUp,
	}
}

func toUniformityResultDTO(calc utypes.UniformityCalculation) UniformityResultDTO {
	return UniformityResultDTO{
		UniformQty: calc.UniformQty,
		OutsideQty: calc.OutsideQty,
		Uniformity: calc.Uniformity,
		Cv:         calc.Cv,
	}
}

func toUniformityDetailItemsDTO(calc utypes.UniformityCalculation) []UniformityDetailItemDTO {
	result := make([]UniformityDetailItemDTO, len(calc.Details))
	for i, item := range calc.Details {
		result[i] = UniformityDetailItemDTO{
			Id:     item.Id,
			Weight: item.Weight,
			Range:  item.Range,
		}
	}
	return result
}

func resolveLocationName(pfk entity.ProjectFlockKandang) string {
	if pfk.Kandang.Id != 0 && pfk.Kandang.Location.Id != 0 {
		return pfk.Kandang.Location.Name
	}
	if pfk.ProjectFlock.Id != 0 && pfk.ProjectFlock.Location.Id != 0 {
		return pfk.ProjectFlock.Location.Name
	}
	return ""
}

func resolveProjectFlockName(pfk entity.ProjectFlockKandang) string {
	if pfk.ProjectFlock.Id != 0 {
		return pfk.ProjectFlock.FlockName
	}
	return ""
}

func resolveKandangName(pfk entity.ProjectFlockKandang) string {
	if pfk.Kandang.Id != 0 {
		return pfk.Kandang.Name
	}
	return ""
}

func formatUniformityDate(date *time.Time) string {
	if date == nil || date.IsZero() {
		return ""
	}
	return utils.FormatDate(*date)
}

func BuildUniformityFilters(query *validation.Query) fiber.Map {
	if query == nil {
		return fiber.Map{}
	}
	return fiber.Map{
		"project_flock_kandang_id": query.ProjectFlockKandangId,
		"start_date":               query.StartDate,
		"end_date":                 query.EndDate,
		"with_chart":               query.WithChart,
		"status":                   "Pengajuan",
	}
}
