package cvt

import (
	"encoding/json"

	coreuser "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/core-server/user/v1"
	apprent "github.com/tidbcloud/devops/entity/approval"
	apprmod "github.com/tidbcloud/devops/models/approval"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	timeutil "github.com/tidbcloud/devops/pkg/infra/utils/time"
)

type m2e struct{}

func (cvt m2e) Approval(m *apprmod.Approval) *apprent.Approval {
	e := &apprent.Approval{
		ID:            m.ID.Uint64(),
		Name:          m.Name,
		Applicant:     m.Applicant,
		StartAt:       timeutil.TimeToTs(m.StartAt),
		EndAt:         timeutil.TimeToTs(m.EndAt),
		Status:        apprent.ApprovalStatus(m.Status),
		DetailLink:    m.DetailLink,
		Parameters:    nil,
		Overview:      nil,
		Reason:        m.Reason,
		Configuration: apprent.ApprovalConfiguration{},
		FromTemplate:  m.FromTemplate,

		Levels:  make([]*apprent.ApprovalLevel, 0),
		CCTasks: make([]*apprent.ApprovalCCTask, 0, len(m.CCTasks)),
	}

	if m.Parameters != nil {
		params := map[string]string{}
		err := json.Unmarshal(m.Parameters, &params)
		if err != nil {
			panic(err)
		}
		e.Parameters = params
	}
	if m.Overview != nil {
		overview := map[string]string{}
		err := json.Unmarshal(m.Overview, &overview)
		if err != nil {
			panic(err)
		}
		e.Overview = overview
	}
	if len(m.Configuration) != 0 {
		err := json.Unmarshal(m.Configuration, &e.Configuration)
		if err != nil {
			panic(err)
		}
	}

	curLevel := 0
	maxLevelInNodes := 0
	for {
		apprLevel := &apprent.ApprovalLevel{
			Level: curLevel,
			Nodes: make([]*apprent.ApprovalNode, 0),
		}
		for _, node := range m.Nodes {
			if node.Level > maxLevelInNodes {
				maxLevelInNodes = node.Level
			}

			if node.Level != curLevel {
				continue
			}

			// find tasks and backup tasks in this node
			tasksInNode := make([]*apprmod.ApprovalTask, 0)
			backupTasksInNode := make([]*apprmod.ApprovalTask, 0)
			backupNodeName := node.Name + backupNodeNameSuffix
			for _, task := range m.Tasks {
				if task.Level != curLevel {
					continue
				}
				if task.NodeName == node.Name {
					tasksInNode = append(tasksInNode, task)
				} else if task.NodeName == backupNodeName {
					backupTasksInNode = append(backupTasksInNode, task)
				}
			}

			apprLevel.Nodes = append(apprLevel.Nodes, cvt.ApprovalNode(node, tasksInNode, backupTasksInNode))
		}

		if len(apprLevel.Nodes) == 0 {
			break
		}

		e.Levels = append(e.Levels, apprLevel)
		curLevel++
	}

	if maxLevelInNodes > curLevel {
		// there are levels missing in the middle
		panic("missing levels in approval nodes")
	}

	for _, cct := range m.CCTasks {
		e.CCTasks = append(e.CCTasks, cvt.ApprovalCCTask(cct))
	}

	for _, at := range m.AdminTasks {
		e.AdminTasks = append(e.AdminTasks, cvt.ApprovalAdminTask(at))
	}

	return e
}

func (cvt m2e) ApprovalNode(m *apprmod.ApprovalNode, tasks []*apprmod.ApprovalTask, backupTasks []*apprmod.ApprovalTask) *apprent.ApprovalNode {
	e := &apprent.ApprovalNode{
		ID:     m.ID.Uint64(),
		Name:   m.Name,
		Method: apprent.ApproveMethod(m.Method),
		Status: apprent.ApprovalNodeStatus(m.Status),
		Tasks:  make([]*apprent.ApprovalTask, 0, len(tasks)),
	}

	for _, t := range tasks {
		e.Tasks = append(e.Tasks, cvt.ApprovalTask(t))
	}
	for _, t := range backupTasks {
		e.BackupTasks = append(e.BackupTasks, cvt.ApprovalTask(t))
	}

	return e
}

func (cvt m2e) ApprovalTask(m *apprmod.ApprovalTask) *apprent.ApprovalTask {
	var notifyLarkType *coreuser.LarkType
	if m.NotifyLarkType != nil {
		notifyLarkType = pointer.Pointer(larkTypeFromModel(*m.NotifyLarkType))
	}

	return &apprent.ApprovalTask{
		ID:              m.ID.Uint64(),
		User:            m.User,
		StartAt:         timeutil.TimeToTs(m.StartAt),
		EndAt:           timeutil.TimeToTs(m.EndAt),
		Status:          apprent.ApprovalTaskStatus(m.Status),
		NotifyMessageID: m.NotifyMessageID,
		NotifyLarkType:  notifyLarkType,
	}
}

func larkTypeFromModel(value string) coreuser.LarkType {
	switch value {
	case coreuser.LarkType_LARK_TYPE_GLOBAL.String(), "global":
		return coreuser.LarkType_LARK_TYPE_GLOBAL
	default:
		return coreuser.LarkType_LARK_TYPE_UNSPECIFIED
	}
}

func (cvt m2e) ApprovalCCTask(m *apprmod.ApprovalCCTask) *apprent.ApprovalCCTask {
	return &apprent.ApprovalCCTask{
		ID:   m.ID.Uint64(),
		User: m.User,
	}
}

func (cvt m2e) ApprovalAdminTask(m *apprmod.ApprovalAdminTask) *apprent.ApprovalAdminTask {
	return &apprent.ApprovalAdminTask{
		ID:     m.ID.Uint64(),
		User:   m.User,
		EndAt:  timeutil.TimeToTs(m.EndAt),
		Status: apprent.ApprovalTaskStatus(m.Status),
	}
}
