package call

import (
	"fmt"
	"math/rand/v2"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	dyvmsapi20170525 "github.com/alibabacloud-go/dyvmsapi-20170525/v4/client"
	util "github.com/alibabacloud-go/tea-utils/v2/service"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/tidbcloud/escalation-base/feishu_messager_v2/api"
	ratelimiter "github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/clients/alicli/call/rate_limiter"
	"github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/config"
	"github.com/tidbcloud/escalation-base/feishu_messager_v2/pkg/infra/errs"
)

type aliCallCli struct {
	rl             ratelimiter.RateLimiter
	dyvmCli        *dyvmsapi20170525.Client
	ttsCode        string
	calloutNumbers []string
}

func (cli *aliCallCli) SingleCall(number string) error {
	var calloutNumber string

	// TODO: Per No Call limiter is not stable, shall not be used in the current version
	/* for _, candidateCalloutNumber := range cli.calloutNumbers {
		if cli.rl.PerCallNo.AllowKey(candidateCalloutNumber + number) {
			calloutNumber = candidateCalloutNumber
		}
	}

	if len(calloutNumber) == 0 {
		return errs.APIErrorf(
			api.ErrCodeRateLimited,
			"rate limit reached for number: %s",
			number,
		)
	} */

	if len(cli.calloutNumbers) == 0 {
		return errs.APIErrorf(api.ErrCodeInternal, "no available callout number")
	} else if len(cli.calloutNumbers) == 1 {
		calloutNumber = cli.calloutNumbers[0]
	} else {
		// choose the callout number randomly
		index := rand.IntN(len(cli.calloutNumbers))
		calloutNumber = cli.calloutNumbers[index]
	}

	// General
	allowed := cli.rl.General.Allow()
	if !allowed {
		return errs.APIErrorf(
			api.ErrCodeRateLimited,
			"burst limit reached for number: %s",
			number,
		)
	}

	// Start Calling
	singleCallByTtsRequest := &dyvmsapi20170525.SingleCallByTtsRequest{
		CalledShowNumber: tea.String(calloutNumber),
		CalledNumber:     tea.String(number),
		TtsCode:          tea.String(cli.ttsCode),
	}
	runtime := &util.RuntimeOptions{}
	tryErr := func() (_e error) {
		defer func() {
			if r := tea.Recover(recover()); r != nil {
				_e = r
			}
		}()
		_, _e = cli.dyvmCli.SingleCallByTtsWithOptions(singleCallByTtsRequest, runtime)
		if _e != nil {
			return _e
		}

		return nil
	}()

	if tryErr != nil {
		return errs.APIErrorf(api.ErrCodeInternal, "call ali voice api failed: %w, number: %s", tryErr, number)
	} else {
		return nil
	}
}

func NewAliCallCli(conf config.AliConfig) (Call, error) {
	config := &openapi.Config{
		AccessKeyId:     tea.String(conf.AccessKeyId),
		AccessKeySecret: tea.String(conf.AccessKeySecret),
	}
	config.Endpoint = tea.String("dyvmsapi.aliyuncs.com")
	dyvmsCli := &dyvmsapi20170525.Client{}
	dyvmsCli, err := dyvmsapi20170525.NewClient(config)
	if err == nil {
		return &aliCallCli{
			rl:             ratelimiter.NewRateLimiter(conf.RateLimit),
			ttsCode:        conf.TtsCode,
			calloutNumbers: conf.CalloutNumbers,
			dyvmCli:        dyvmsCli,
		}, nil
	} else {
		return nil, fmt.Errorf("initiating dyvmsapi client failed: %w", err)
	}
}
