package manualaction

import (
	"context"
	"fmt"
	"strings"

	userv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/user/v1"
	opent "github.com/tidbcloud/devops/entity/operation"
	larknotify "github.com/tidbcloud/devops/pkg/infra/clients/larkcli/notify"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	"github.com/tidbcloud/devops/pkg/service/operation/actions"
)

var (
	ActionNotifyOncaller opent.ActionMetadata = opent.ActionMetadata{
		Name: "NotifyOncaller",
		Type: opent.ActionTypeManual,
	}
)

const ParamOncallTeamRef opent.ParamName = "oncall_team_ref"

type notifyOncallerParam struct {
	Operation     string
	OncallTeamRef string
}

func NewNotifyOncaller(comps *actions.Components) actions.MaunalAction {
	return &notifyOncaller{comps: comps}
}

type notifyOncaller struct {
	actions.BaseAction
	comps *actions.Components
}

func (a *notifyOncaller) Metadata() opent.ActionMetadata {
	return ActionNotifyOncaller
}

func (a *notifyOncaller) PreCheck(ctx context.Context, instance *opent.Instance) error {
	_, err := a.getParam(instance)
	return err
}

func (a *notifyOncaller) Start(ctx context.Context, instance *opent.Instance) (*actions.ActionResult, error) {
	param, err := a.getParam(instance)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	if a.comps.OnCallCli == nil {
		return actions.FailedActionResult(fmt.Errorf("oncall client is not configured")), nil
	}

	resp, err := a.comps.OnCallCli.GetCurrentOnCall(ctx, param.OncallTeamRef)
	if err != nil {
		return actions.FailedActionResult(fmt.Errorf("get current oncall failed: %w", err)), nil
	}

	oncallers := resp.CurrentEngineerEmails
	if len(oncallers) == 0 {
		return actions.FailedActionResult(fmt.Errorf("no oncallers found for team '%s'", param.OncallTeamRef)), nil
	}

	larkParam := larknotify.OperationNotifyParam{
		InstanceID: instance.ID,
		Operation:  param.Operation,
		Reason:     instance.Reason,
		Params:     instance.Params,
		DetailURL:  actions.InstanceDetailLink(instance.ID),
		Applicant: larknotify.LarkUser{
			Email:  instance.Applicant,
			LarkID: nil,
		},
	}

	applResp, err := a.comps.UserAPI.GetUser(ctx, &userv1.GetUserRequest{Email: instance.Applicant})
	if err != nil {
		return actions.FailedActionResult(err), nil
	}
	larkParam.Applicant.LarkID = pointer.Pointer(applResp.User.LarkUserOpenId)
	for _, oncaller := range oncallers {
		oncallerParam := larknotify.LarkUser{Email: strings.ToLower(oncaller)}

		userResp, err := a.comps.UserAPI.GetUser(ctx, &userv1.GetUserRequest{Email: oncaller})
		if err != nil {
			a.comps.Logger.Info("get user failed when send operation notify", "err", err.Error(), "operation", param.Operation)
		} else {
			oncallerParam.LarkID = pointer.Pointer(userResp.User.LarkUserOpenId)
		}

		larkParam.OnCallers = append(larkParam.OnCallers, oncallerParam)
	}

	err = a.comps.LarkCli.Notify.SendMessageViaWebhook(ctx, a.comps.Conf.OperationNotifiyWehookURL, larkParam)
	if err != nil {
		return actions.FailedActionResult(err), nil
	}

	return actions.RunningActionResult("notified oncallers to execute this operation", nil), nil
}

func (a *notifyOncaller) OnFinish(ctx context.Context, instance *opent.Instance, result opent.ActionStatus, output string) error {
	return nil
}

func (a *notifyOncaller) getParam(instance *opent.Instance) (*notifyOncallerParam, error) {
	param := &notifyOncallerParam{}
	param.Operation = instance.Operation

	teamRef, _ := instance.GetParam(ParamOncallTeamRef)
	if teamRef == "" {
		return nil, fmt.Errorf("oncall_team_ref param is required")
	}
	param.OncallTeamRef = teamRef

	return param, nil
}
