package action

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	privent "github.com/tidbcloud/devops/entity/privilege"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	apprapi "github.com/tidbcloud/devops/pkg/service/approval/api"
	"github.com/tidbcloud/devops/pkg/service/privilege/api"
)

const (
	ApprovalTemplateID = "privilege/request-privilege"
)

type actorCreate struct {
	*baseActor
}

func (a *actorCreate) Request(param api.RequestPrivilegeParam) error {
	ctx := a.Context

	// create in db
	newPriv := a.newBasePrivilege(param)
	if param.Approval != nil {
		newPriv.Type = privent.PrivilegeTypeApproval
		newPriv.Content.Approval = &privent.ApprovalPrivilegeContent{
			Scopes: param.Approval.Scopes,
		}
	}

	created, err := a.Repo.Create(ctx, newPriv)
	if err != nil {
		return fmt.Errorf("create privilege failed: %w", err)
	}
	a.Privilege = created

	// send an approval
	approvalID, err := a.sendApproval()
	if err != nil {
		return err
	}
	a.Privilege.ApprovalID = *approvalID
	a.Privilege.Phase = privent.PrivilegePhaseApproving
	_, err = a.Repo.Update(ctx, a.Privilege)
	if err != nil {
		return fmt.Errorf("update privilege failed: %w", err)
	}

	return nil
}

func (a *actorCreate) newBasePrivilege(param api.RequestPrivilegeParam) *privent.Privilege {
	newPriv := &privent.Privilege{
		ID:         0,
		Type:       "",
		Source:     param.Source,
		Reason:     param.Reason,
		ExpiredAt:  param.ExpiredAt,
		Applicant:  param.Applicant,
		Phase:      privent.PrivilegePhaseCreated,
		ApprovalID: 0,
	}

	for _, user := range param.BindingUsers {
		newPriv.Bindings = append(newPriv.Bindings, &privent.PrivilegeBinding{
			ID:   0,
			User: user,
		})
	}

	return newPriv
}

func (a *actorCreate) sendApproval() (*uint64, error) {
	ctx := a.Context
	priv := a.Privilege

	param := apprapi.CreateApprovalFromTemplateParam{
		CreateApprovalParamBase: apprapi.CreateApprovalParamBase{
			Name:       "OpsPortal Privilege Request",
			Applicant:  priv.Applicant,
			DetailLink: DetailLink(priv.ID),
			Parameters: map[string]string{
				"id":          strconv.FormatUint(priv.ID, 10),
				"type":        string(priv.Type),
				"expire_time": time.Unix(priv.ExpiredAt, 0).Format(time.RFC3339),
			},
			Reason: priv.Reason,
		},
		FromTemplate: ApprovalTemplateID,
	}

	users := container.TransformSlice(priv.Bindings, func(binding *privent.PrivilegeBinding) string {
		return binding.User
	})
	param.Parameters["binding_users"] = "[" + strings.Join(users, ", ") + "]"

	if priv.Type == privent.PrivilegeTypeApproval {
		scopes := container.TransformSlice(priv.Content.Approval.Scopes, container.ToString)
		param.Parameters["approval_scopes"] = "[" + strings.Join(scopes, ", ") + "]"
		param.Parameters["description"] = "Approvals within these scopes will be skipped"
	}

	apprID, err := a.ApprovalService.CreateApprovalFromTemplate(ctx, param)
	if err != nil {
		return nil, fmt.Errorf("create approval for privilege failed: %w", err)
	}

	return apprID, nil
}
