//go:generate ${GOBIN}/mockgen -destination approval_service_mock.go -package=api ${GO_MODULE}/pkg/service/approval/api ApprovalServiceAPI
package api

import (
	"context"
	"fmt"

	apprent "github.com/tidbcloud/devops/entity/approval"
)

type CreateApprovalParamBase struct {
	// Name is the approval's name used to categorize the approval.
	Name string `json:"name"`
	// Applicant is the user who applies for the approval.
	Applicant string `json:"applicant"`
	// DetailLink is a link showed in the notifcation.
	DetailLink string `json:"detail_link"`
	// Parameters is the full params used for approval routing (fromParam/evalCondition).
	Parameters map[string]string `json:"parameters"`
	// Overview is a human-readable summary shown in Lark Notifications.
	Overview map[string]string `json:"overview,omitempty"`
	// Reason is the reason showed in the notifcation.
	Reason string `json:"reason"`
	// Configuration is the approval configuration.
	Configuration apprent.ApprovalConfiguration `json:"configuration"`
}

type CreateApprovalParam struct {
	CreateApprovalParamBase `json:",inline"`

	// Nodes is the approval nodes.
	//
	// If it is empty, the approval will be approved after created.
	Nodes []*apprent.ApproverNode `json:"nodes"`
	// CCTasks defines the users who will be notified when the approval request is **Approved**.
	CCTasks *apprent.Approver `json:"cc_tasks"`
	// AdminTasks defines the users who can approve or reject the approval at any time.
	AdminTasks *apprent.Approver `json:"admin_tasks"`
}

func (p CreateApprovalParam) Validate() error {
	for _, node := range p.Nodes {
		if len(node.Parallel) == 0 {
			if node.Approver == nil {
				return fmt.Errorf("approver is required for non-parallel node")
			}
		} else {
			if node.Approver != nil {
				return fmt.Errorf("approver must be nil for parallel node")
			}
		}
	}

	return nil
}

type CreateApprovalFromTemplateParam struct {
	CreateApprovalParamBase `json:",inline"`

	// The template id used to create the approval
	FromTemplate string `json:"from_template"`
}

type FinishApprovalTaskParam struct {
	User   string
	Result apprent.ApprovalTaskStatus
}

type ApprovalPermission struct {
	CanApprove      bool
	CanAdminApprove bool
}

type ApprovalServiceAPI interface {
	// GetApproval returns the approval by the id.
	GetApproval(ctx context.Context, id uint64) (*apprent.Approval, error)
	// CheckApprovalPermission checks if the user can approve or admin-approve the approval.
	CheckApprovalPermission(ctx context.Context, id uint64, userEmail string) (*ApprovalPermission, error)
	// CreateApproval creates a new approval.
	CreateApproval(ctx context.Context, param CreateApprovalParam) (*uint64, error)
	// CreateApprovalFromTemplate creates a new approval from a template.
	//
	// If the `Nodes` or `CCTasks` is not empty, the template will be ignored.
	CreateApprovalFromTemplate(ctx context.Context, param CreateApprovalFromTemplateParam, mutates ...func(param *CreateApprovalParam)) (*uint64, error)
	// FinishApprovalTask finishes a approval task of a approval.
	FinishApprovalTask(ctx context.Context, id uint64, param FinishApprovalTaskParam) error
	// FinishApprovalAdminTask finishes an admin approval task of a approval.
	FinishApprovalAdminTask(ctx context.Context, id uint64, param FinishApprovalTaskParam) error
	// CancelApproval cancels a approval.
	CancelApproval(ctx context.Context, id uint64) error

	// RegisterTemplate registers an approval template.
	RegisterTemplate(ctx context.Context, tmpl apprent.Template) error
}
