package entity

import coreuser "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/core-server/user/v1"

type (
	ApproveMethod      string
	ApprovalStatus     string
	ApprovalNodeStatus string
	ApprovalTaskStatus string
)

const (
	ApproveMethodAll ApproveMethod = "ALL"
	ApproveMethodAny ApproveMethod = "ANY"

	ApprovalStatusPending   ApprovalStatus = "PENDING"
	ApprovalStatusApproved  ApprovalStatus = "APPROVED"
	ApprovalStatusRejected  ApprovalStatus = "REJECTED"
	ApprovalStatusCancelled ApprovalStatus = "CANCELED"

	ApprovalNodeStatusPending   ApprovalNodeStatus = "PENDING"
	ApprovalNodeStatusApproved  ApprovalNodeStatus = "APPROVED"
	ApprovalNodeStatusRejected  ApprovalNodeStatus = "REJECTED"
	ApprovalNodeStatusCancelled ApprovalNodeStatus = "CANCELED"

	ApprovalTaskStatusPending  ApprovalTaskStatus = "PENDING"
	ApprovalTaskStatusApproved ApprovalTaskStatus = "APPROVED"
	ApprovalTaskStatusRejected ApprovalTaskStatus = "REJECTED"
	ApprovalTaskStatusDone     ApprovalTaskStatus = "Done"
)

var (
	ApproveMethodValues = []ApproveMethod{
		ApproveMethodAll, ApproveMethodAny,
	}

	ApprovalStatusValues = []ApprovalStatus{
		ApprovalStatusPending, ApprovalStatusApproved,
		ApprovalStatusRejected, ApprovalStatusCancelled,
	}
	ApprovalStatusFinishedValues = []ApprovalStatus{
		ApprovalStatusApproved, ApprovalStatusRejected, ApprovalStatusCancelled,
	}

	ApprovalNodeStatusValues = []ApprovalNodeStatus{
		ApprovalNodeStatusPending, ApprovalNodeStatusApproved,
		ApprovalNodeStatusRejected, ApprovalNodeStatusCancelled,
	}

	ApprovalTaskStatusValues = []ApprovalTaskStatus{
		ApprovalTaskStatusPending, ApprovalTaskStatusApproved, ApprovalTaskStatusRejected,
		ApprovalTaskStatusDone,
	}
)

type ApprovalTask struct {
	ID              uint64             `json:"id"`
	User            string             `json:"user_email"` // email
	StartAt         *int64             `json:"start_at"`
	EndAt           *int64             `json:"end_at"`
	Status          ApprovalTaskStatus `json:"status"`
	NotifyMessageID *string            `json:"notify_message_id"`
	NotifyLarkType  *coreuser.LarkType `json:"notify_lark_type"`
}

type ApprovalCCTask struct {
	ID   uint64 `json:"id"`
	User string `json:"user_email"` // email
}

type ApprovalAdminTask struct {
	ID     uint64             `json:"id"`
	User   string             `json:"user_email"` // email
	EndAt  *int64             `json:"end_at"`
	Status ApprovalTaskStatus `json:"status"`
}

type ApprovalNode struct {
	ID          uint64             `json:"id"`   // id in database
	Name        string             `json:"name"` // Default to "Node {Level}-{Index}"
	Method      ApproveMethod      `json:"method"`
	Tasks       []*ApprovalTask    `json:"tasks"`
	BackupTasks []*ApprovalTask    `json:"backup_tasks,omitempty"`
	Status      ApprovalNodeStatus `json:"status"`
}

func (n *ApprovalNode) EvalNodeStatus() ApprovalNodeStatus {
	allResolved := true
	anyApproved := false
	anyRejected := false

	for _, task := range n.Tasks {
		resolved := task.Status == ApprovalTaskStatusApproved || task.Status == ApprovalTaskStatusDone
		allResolved = allResolved && resolved
		anyApproved = anyApproved || task.Status == ApprovalTaskStatusApproved
		anyRejected = anyRejected || task.Status == ApprovalTaskStatusRejected
	}
	for _, task := range n.BackupTasks {
		anyApproved = anyApproved || task.Status == ApprovalTaskStatusApproved
		anyRejected = anyRejected || task.Status == ApprovalTaskStatusRejected
	}
	if anyRejected {
		return ApprovalNodeStatusRejected
	}
	if anyApproved && n.Method == ApproveMethodAny {
		return ApprovalNodeStatusApproved
	}
	if allResolved && anyApproved && n.Method == ApproveMethodAll {
		return ApprovalNodeStatusApproved
	}

	return ApprovalNodeStatusPending
}

type ApprovalLevel struct {
	Level int             `json:"level"` // first is 0, MUST be equal to the index of slice
	Nodes []*ApprovalNode `json:"nodes"`
}

func (l *ApprovalLevel) EvalLevelStatus() ApprovalNodeStatus {
	allApproved := true
	anyRejected := false

	for _, node := range l.Nodes {
		allApproved = allApproved && node.Status == ApprovalNodeStatusApproved
		anyRejected = anyRejected || node.Status == ApprovalNodeStatusRejected
	}
	if anyRejected {
		return ApprovalNodeStatusRejected
	}
	if allApproved {
		return ApprovalNodeStatusApproved
	}

	return ApprovalNodeStatusPending
}

type ApprovalConfiguration struct {
	SkipApplicantNotification bool `json:"skip_applicant_notification"`
}

type Approval struct {
	ID uint64 `json:"id"`
	// Name is the name of the approval request used to categorize approvals.
	Name string `json:"name"`
	// Applicant is the user email who created the approval request.
	Applicant string `json:"applicant"`
	// StartAt is the time when the approval request is created.
	StartAt *int64 `json:"start_at"`
	// EndAt is the time when the approval request is ended.
	EndAt *int64 `json:"end_at"`
	// Status is the status of the approval request.
	Status ApprovalStatus `json:"status"`
	// DetailLink is the link to the detail page for this request.
	//
	// It is used in the notification message.
	DetailLink string `json:"detail_link"`
	// Parameters is the full operation params used for routing (fromParam/evalCondition).
	Parameters map[string]string `json:"parameters"`
	// Overview is a persisted human-readable summary shown in Lark notifications (alias, target name, etc.).
	Overview map[string]string `json:"overview,omitempty"`
	// Reason is the description of the approval request.
	Reason string `json:"reason"`
	// Configuration is the approval configuration.
	Configuration ApprovalConfiguration `json:"configuration"`
	// FromTemplate is the template id used to create the approval
	FromTemplate *string `json:"from_template"`
	// Levels defines the approval workflow.
	Levels []*ApprovalLevel `json:"levels"`
	// CCTasks defines the users who will be notified when the approval request is **Approved**.
	CCTasks []*ApprovalCCTask `json:"cc_tasks"`
	// AdminTasks is the users who can approve/reject/cancel the approval request at any time.
	AdminTasks []*ApprovalAdminTask `json:"admin_tasks"`
}

func (a *Approval) GetLevel(level int) *ApprovalLevel {
	for _, apprLevel := range a.Levels {
		if apprLevel.Level == level {
			return apprLevel
		}
	}
	return nil
}
