package dto

import (
	"sort"
	"strings"
	"time"

	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
	approvalutils "gitlab.com/mbugroup/lti-api.git/internal/utils/approvals"
)

type ApprovalRelationDTO struct {
	Id         uint                    `json:"id"`
	StepNumber uint16                  `json:"step_number"`
	StepName   string                  `json:"step_name"`
	Action     *string                 `json:"action"`
	Notes      *string                 `json:"notes"`
	ActionBy   userDTO.UserRelationDTO `json:"action_by"`
	ActionAt   time.Time               `json:"action_at"`
}

type ApprovalGroupDTO struct {
	StepNumber uint16                `json:"step_number"`
	StepName   string                `json:"step_name"`
	Approvals  []ApprovalRelationDTO `json:"approvals"`
}

func ToApprovalDTO(e entity.Approval) ApprovalRelationDTO {
	dto := ApprovalRelationDTO{
		Id:    e.Id,
		Notes: e.Notes,
	}

	if e.StepNumber > 0 {
		stepCopy := uint16(e.StepNumber)
		dto.StepNumber = stepCopy
	}

	stepName := strings.TrimSpace(e.StepName)
	if stepName == "" && e.ApprovableType != "" && e.StepNumber > 0 {
		if label, ok := approvalutils.ApprovalStepName(approvalutils.ApprovalWorkflowKey(e.ApprovableType), approvalutils.ApprovalStep(e.StepNumber)); ok {
			stepName = label
		}
	}
	dto.StepName = stepName

	if e.Action != nil {
		value := strings.TrimSpace(string(*e.Action))
		if value != "" {
			valueCopy := value
			dto.Action = &valueCopy
		}
	}

	if e.ActionUser != nil && e.ActionUser.Id != 0 {
		user := userDTO.ToUserRelationDTO(*e.ActionUser)
		dto.ActionBy = user
	} else if e.ActionBy != nil && *e.ActionBy != 0 {
		dto.ActionBy = userDTO.UserRelationDTO{
			Id:     *e.ActionBy,
			IdUser: int64(*e.ActionBy),
		}
	}

	if !e.ActionAt.IsZero() {
		at := e.ActionAt
		dto.ActionAt = at
	}

	return dto
}

func ToApprovalDTOs(items []entity.Approval) []ApprovalRelationDTO {
	result := make([]ApprovalRelationDTO, len(items))
	for i, item := range items {
		result[i] = ToApprovalDTO(item)
	}
	return result
}

func ToApprovalGroupDTOs(items []entity.Approval) []ApprovalGroupDTO {
	if len(items) == 0 {
		return nil
	}

	type groupAccumulator struct {
		StepName  string
		Approvals []ApprovalRelationDTO
	}

	groups := make(map[uint16]*groupAccumulator)
	order := make([]uint16, 0)
	for _, item := range items {
		step := item.StepNumber
		acc, exists := groups[step]
		if !exists {
			stepName := strings.TrimSpace(item.StepName)
			if stepName == "" && item.ApprovableType != "" && item.StepNumber > 0 {
				if label, ok := approvalutils.ApprovalStepName(approvalutils.ApprovalWorkflowKey(item.ApprovableType), approvalutils.ApprovalStep(item.StepNumber)); ok {
					stepName = label
				}
			}
			acc = &groupAccumulator{StepName: stepName}
			groups[step] = acc
			order = append(order, step)
		}
		acc.Approvals = append(acc.Approvals, ToApprovalDTO(item))
	}

	sort.Slice(order, func(i, j int) bool { return order[i] < order[j] })

	result := make([]ApprovalGroupDTO, len(order))
	for i, step := range order {
		acc := groups[step]
		result[i] = ApprovalGroupDTO{
			StepNumber: step,
			StepName:   acc.StepName,
			Approvals:  acc.Approvals,
		}
	}

	return result
}
