package dto

import (
	"time"

	"gitlab.com/mbugroup/lti-api.git/internal/config"
	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"
	kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
	locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
	nonstockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/dto"
	productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"

	// pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
	"gitlab.com/mbugroup/lti-api.git/internal/utils"
	approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
)

type ProjectFlockRelationDTO struct {
	Id        uint   `json:"id"`
	Period    int    `json:"period"`
	FlockName string `json:"flock_name"`
}

type ProjectFlockListDTO struct {
	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"`
	Kandangs           []KandangWithProjectFlockIdDTO                       `json:"kandangs,omitempty"`
	ProjectBudgets     []ProjectBudgetDTO                                   `json:"project_budgets,omitempty"`
	CreatedUser        *userDTO.UserRelationDTO                             `json:"created_user"`
	CreatedAt          time.Time                                            `json:"created_at"`
	UpdatedAt          time.Time                                            `json:"updated_at"`
	Approval           approvalDTO.ApprovalRelationDTO                      `json:"approval"`
}

type KandangWithProjectFlockIdDTO struct {
	kandangDTO.KandangRelationDTO
	ProjectFlockKandangId uint `json:"project_flock_kandang_id"`
	Period                int  `json:"period"`
	ClosedAt              *time.Time `json:"closed_at,omitempty"`
}

type ProjectFlockDetailDTO struct {
	ProjectFlockListDTO
}

type KandangPeriodSummaryDTO struct {
	Id     uint   `json:"id"`
	Name   string `json:"name"`
	Period int    `json:"period"`
}

type ProjectBudgetDTO struct {
	Id       uint                             `json:"id"`
	Qty      float64                          `json:"qty"`
	Price    float64                          `json:"price"`
	Nonstock *nonstockDTO.NonstockRelationDTO `json:"nonstock,omitempty"`
}

func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectFlockListDTO {
	var createdUser *userDTO.UserRelationDTO
	if e.CreatedUser.Id != 0 {
		mapped := userDTO.ToUserRelationDTO(e.CreatedUser)
		createdUser = &mapped
	}

	var kandangSummaries []KandangWithProjectFlockIdDTO
	if len(e.Kandangs) > 0 {
		kandangSummaries = make([]KandangWithProjectFlockIdDTO, len(e.Kandangs))
		for i, kandang := range e.Kandangs {

			var (
				pfkId    uint
				period   int
				closedAt *time.Time
			)
			for _, kh := range e.KandangHistory {
				if kh.KandangId == kandang.Id {
					pfkId = kh.Id
					period = kh.Period
					closedAt = kh.ClosedAt
					break
				}
			}
			mapped := kandangDTO.ToKandangRelationDTO(kandang)
			if closedAt != nil {
				// Jangan ubah tabel kandang, hanya override status di response.
				mapped.Status = string(utils.KandangStatusNonActive)
			}
			kandangSummaries[i] = KandangWithProjectFlockIdDTO{
				KandangRelationDTO:    mapped,
				ProjectFlockKandangId: pfkId,
				Period:                period,
				ClosedAt:              closedAt,
			}
		}
	}

	var areaSummary *areaDTO.AreaRelationDTO
	if e.Area.Id != 0 {
		mapped := areaDTO.ToAreaRelationDTO(e.Area)
		areaSummary = &mapped
	}

	var productionStandardSummary *productionStandardDTO.ProductionStandardRelationDTO
	if e.ProductionStandard.Id != 0 {
		mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProductionStandard)
		productionStandardSummary = &mapped
	}

	var locationSummary *locationDTO.LocationRelationDTO
	if e.Location.Id != 0 {
		mapped := locationDTO.ToLocationRelationDTO(e.Location)
		locationSummary = &mapped
	}

	latestApproval := defaultProjectFlockLatestApproval(e)
	if e.LatestApproval != nil {
		snapshot := approvalDTO.ToApprovalDTO(*e.LatestApproval)
		latestApproval = snapshot
	}

	return ProjectFlockListDTO{
		ProjectFlockRelationDTO: createProjectFlockRelationDTO(e, period),
		Area:                    areaSummary,
		Kandangs:                kandangSummaries,
		ProjectBudgets:          ToProjectBudgetDTOs(e.Budgets),
		Category:                e.Category,
		StandardFcr:             resolveProjectFlockStandardFcr(e),
		ProductionStandard:      productionStandardSummary,
		Location:                locationSummary,
		CreatedAt:               e.CreatedAt,
		UpdatedAt:               e.UpdatedAt,
		CreatedUser:             createdUser,
		Approval:                latestApproval,
	}
}

func ToProjectFlockListDTOs(items []entity.ProjectFlock) []ProjectFlockListDTO {
	result := make([]ProjectFlockListDTO, len(items))
	for i, item := range items {
		result[i] = ToProjectFlockListDTOWithPeriod(item, 0)
	}
	return result
}

func ToProjectFlockListDTO(e entity.ProjectFlock) ProjectFlockListDTO {
	return ToProjectFlockListDTOWithPeriod(e, 0)
}

func ToProjectFlockListDTOsWithPeriods(items []entity.ProjectFlock, periods map[uint]int) []ProjectFlockListDTO {
	result := make([]ProjectFlockListDTO, len(items))
	for i, item := range items {
		p := 0
		if periods != nil {
			if v, ok := periods[item.Id]; ok {
				p = v
			}
		}
		result[i] = ToProjectFlockListDTOWithPeriod(item, p)
	}
	return result
}

func ToProjectFlockDetailDTO(e entity.ProjectFlock) ProjectFlockDetailDTO {
	return ProjectFlockDetailDTO{
		ProjectFlockListDTO: ToProjectFlockListDTOWithPeriod(e, 0),
	}
}

func defaultProjectFlockLatestApproval(e entity.ProjectFlock) approvalDTO.ApprovalRelationDTO {
	result := approvalDTO.ApprovalRelationDTO{}

	step := utils.ProjectFlockStepPengajuan
	if step > 0 {
		result.StepNumber = uint16(step)
		if label, ok := approvalutils.ApprovalStepName(utils.ApprovalWorkflowProjectFlock, step); ok {
			result.StepName = label
		} else if label, ok := utils.ProjectFlockApprovalSteps[step]; ok {
			result.StepName = label
		}
	}

	if e.CreatedUser.Id != 0 {
		result.ActionBy = userDTO.ToUserRelationDTO(e.CreatedUser)
	} else if e.CreatedBy != 0 {
		result.ActionBy = userDTO.UserRelationDTO{
			Id:     e.CreatedBy,
			IdUser: int64(e.CreatedBy),
		}
	}

	return result
}

func createProjectFlockRelationDTO(e entity.ProjectFlock, period int) ProjectFlockRelationDTO {
	return ProjectFlockRelationDTO{
		Id:        e.Id,
		Period:    period,
		FlockName: e.FlockName,
	}
}

func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 {
	if e.ProductionStandard.Id == 0 || len(e.ProductionStandard.ProductionStandardDetails) == 0 {
		return nil
	}
	week := 1
	if e.Category == string(utils.ProjectFlockCategoryLaying) {
		week = config.LayingWeekStart()
	}
	for _, detail := range e.ProductionStandard.ProductionStandardDetails {
		if detail.Week == week && detail.StandardFCR != nil {
			return detail.StandardFCR
		}
	}
	return nil
}

func ToProjectBudgetDTO(e entity.ProjectBudget) ProjectBudgetDTO {
	var nonstockRef *nonstockDTO.NonstockRelationDTO
	if e.Nonstock != nil && e.Nonstock.Id != 0 {
		mapped := nonstockDTO.ToNonstockRelationDTO(*e.Nonstock)
		nonstockRef = &mapped
	}

	return ProjectBudgetDTO{
		Id:       e.Id,
		Qty:      e.Qty,
		Price:    e.Price,
		Nonstock: nonstockRef,
	}
}

func ToProjectBudgetDTOs(e []entity.ProjectBudget) []ProjectBudgetDTO {
	result := make([]ProjectBudgetDTO, len(e))
	for i, r := range e {
		result[i] = ToProjectBudgetDTO(r)
	}
	return result
}
