package aws

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

	"github.com/tidbcloud/qa-gourd/ProductModel"
	"github.com/tidbcloud/qa-gourd/ProductService"
	"github.com/tidbcloud/qa-gourd/handler"

	l4g "github.com/alecthomas/log4go"
	"github.com/avast/retry-go"
	"github.com/dailymotion/allure-go"
	"github.com/tidbcloud/qa-PlatformAPIAuto/common"
	"github.com/tidbcloud/qa-PlatformAPIAuto/util"
	"github.com/tidbcloud/qa-PlatformAPIAuto/webScene/br"
	dpCommon "github.com/tidbcloud/qa-PlatformAPIAuto/webScene/common"
)

func ExecuteFinalBackupCase(t *testing.T, ws ...*ProductService.WebRequestInfo) {
	uniqueKey := fmt.Sprintf("%s_%s",
		dpCommon.SCHEMA_FINAL_BACKUP_CASE, util.FnvHash(common.Envn+string(dpCommon.CloudProviderTypeAWS)+common.GetDbassRequestInfo(t).CommonEnv.Email))
	webRequestInfo := ProductService.GetWebAPIRequestInfo(&testing.T{}, ProductModel.PoC)
	for _, w := range ws {
		webRequestInfo = w
	}
	test := &BRFinalBackupSceneTest{
		br.CommonSceneTest{
			SceneTest: dpCommon.SceneTest{
				T:                t,
				WebRequestInfo:   webRequestInfo,
				DbaasRequestInfo: common.NewDbassRequestInfo(t),
				TestSchemaName:   uniqueKey,
			},
		},
	}
	test.DbaasRequestInfo.ReSetHeaderToken(t, webRequestInfo.User.TenantEmail)
	test.Run()
}

type BRFinalBackupSceneTest struct {
	br.CommonSceneTest
}

func (s *BRFinalBackupSceneTest) Run() {
	allure.Test(s.T,
		allure.Epic(fmt.Sprintf("%s-%s", dpCommon.CloudProviderTypeAWS, dpCommon.FinalBackupCase)),
		allure.Feature(string(dpCommon.FinalBackupCase)),
		allure.Description(string(dpCommon.FinalBackupCase)),
		allure.Action(func() {
			s.AddCredit()
			s.CreatePOCCluster()
			s.ReclaimCredits()
			s.CheckCluster(true)
			afterThreeTime := time.Now().UTC().AddDate(0, 0, 3).Add(1 * time.Hour).Format("2006-01-02T15:04:05Z")
			s.SetExpirationTime(afterThreeTime)
			s.CheckCluster(false)
			s.CheckBackup(true)
			afterTenTime := time.Now().UTC().AddDate(0, 0, 10).Add(1 * time.Hour).Format("2006-01-02T15:04:05Z")
			s.SetExpirationTime(afterTenTime)
			s.CheckBackup(false)
		}))
}

func (s *BRFinalBackupSceneTest) CreatePOCCluster() {
	allure.Step(
		allure.Description("create poc cluster"),
		allure.Action(func() {
			err := retry.Do(
				func() error {
					_, err := s.WebRequestInfo.CreateMinDedicatedCluster()
					if err != nil {
						return err
					}
					return nil
				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*10),
				retry.Attempts(6),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}

func (s *BRFinalBackupSceneTest) AddCredit() {
	allure.Step(
		allure.Description("add credit"),
		allure.Action(func() {
			pathParam := map[string]string{
				"org_id": s.WebRequestInfo.User.OrgId,
			}
			yesterday := time.Now().UTC().AddDate(0, 0, -1)
			startTime := yesterday.Format("2006-01-02T15:04:05Z")
			tomorrow := time.Now().UTC().AddDate(0, 0, 1)
			endTime := tomorrow.Format("2006-01-02T15:04:05Z")
			addCreditBody := common.NewAddCredits(startTime, endTime)
			l4g.Info("++++++++++++")

			err := retry.Do(
				func() error {
					l4g.Info(addCreditBody)
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, handler.OtherUserInfo.OpsAdminEmail)
					callResp := s.DbaasRequestInfo.DbassRequest(s.T, "addCredits", addCreditBody, pathParam, nil, nil)
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, s.WebRequestInfo.User.TenantEmail)
					httpInfo := common.ParseHttpInfo(callResp)
					if httpInfo.RespStatusCode != common.StatusOK {
						return fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
					}
					s.CreditsPackageId = int(callResp.JSON().Path("$.credits_package_id").Number().Raw())
					l4g.Info(callResp.JSON().Raw())
					l4g.Info(s.CreditsPackageId)
					return nil
				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*10),
				retry.Attempts(6),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}

func (s *BRFinalBackupSceneTest) ReclaimCredits() {
	allure.Step(
		allure.Description("Reclaim credit"),
		allure.Action(func() {
			pathParam := map[string]string{
				"org_id":     s.WebRequestInfo.User.OrgId,
				"package_id": fmt.Sprintf("%d", s.CreditsPackageId),
			}
			err := retry.Do(
				func() error {
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, handler.OtherUserInfo.OpsAdminEmail)
					callResp := s.DbaasRequestInfo.DbassRequest(s.T, "reclaimCredits", nil, pathParam, nil, nil)
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, s.WebRequestInfo.User.TenantEmail)
					httpInfo := common.ParseHttpInfo(callResp)
					if httpInfo.RespStatusCode != common.StatusOK {
						return fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
					}
					return nil
				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*10),
				retry.Attempts(6),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}

func (s *BRFinalBackupSceneTest) SetExpirationTime(runTime string) {
	allure.Step(
		allure.Description("Set Expiration Time"),
		allure.Action(func() {
			tenantId, _ := strconv.Atoi(s.WebRequestInfo.User.OrgId)
			body := common.SetPOCExpireTime{
				Action: "poc_cleanup", RunTime: runTime, TenantId: tenantId}
			err := retry.Do(
				func() error {
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, handler.OtherUserInfo.OpsAdminEmail)
					callResp := s.DbaasRequestInfo.DbassRequest(s.T, "setBillingWorkerExpireTime", body, nil, nil, nil)
					s.DbaasRequestInfo.ReSetHeaderToken(s.T, s.WebRequestInfo.User.TenantEmail)
					httpInfo := common.ParseHttpInfo(callResp)
					if httpInfo.RespStatusCode != common.StatusOK {
						return fmt.Errorf("status code is: %d, http details is: %s", httpInfo.RespStatusCode, httpInfo.ToString())
					}
					return nil
				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*10),
				retry.Attempts(6),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}

func (s *BRFinalBackupSceneTest) CheckCluster(exist bool) {
	allure.Step(
		allure.Description("check cluster is exist"),
		allure.Action(func() {
			pathParam := map[string]string{
				"org_id":     s.WebRequestInfo.User.OrgId,
				"project_id": s.WebRequestInfo.User.ProjectId,
				"cluster_id": s.WebRequestInfo.Cluster.ClusterId,
			}
			err := retry.Do(
				func() error {
					callResp := s.DbaasRequestInfo.DbassRequest(s.T, "getClusterInfo", nil, pathParam, nil, nil)
					httpInfo := common.ParseHttpInfo(callResp)
					err_code := callResp.JSON().Path("$.base_resp.err_code").String().Raw()
					if (httpInfo.RespStatusCode != common.StatusOK || err_code != "0") && exist {
						return fmt.Errorf("CheckCluster failed, exist:%t, status code is: %d, err_code is: %s, "+
							"http details is: %s", exist, httpInfo.RespStatusCode, err_code, httpInfo.ToString())
					}
					return nil
				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*10),
				retry.Attempts(6),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}

func (s *BRFinalBackupSceneTest) CheckBackup(exist bool) {
	allure.Step(
		allure.Description("check backup is exist"),
		allure.Action(func() {
			pathParam := map[string]string{
				"org_id":     s.WebRequestInfo.User.OrgId,
				"project_id": s.WebRequestInfo.User.ProjectId,
				"cluster_id": s.WebRequestInfo.Cluster.ClusterId,
			}
			err := retry.Do(
				func() error {
					callResp := s.DbaasRequestInfo.DbassRequest(s.T, "getBackupList", nil, pathParam, nil, nil)
					httpInfo := common.ParseHttpInfo(callResp)
					if httpInfo.RespStatusCode != common.StatusOK {
						return fmt.Errorf("CheckBackup failed, exist: %t, status code is: %d, http details is: %s", exist, httpInfo.RespStatusCode, httpInfo.ToString())
					} else if callResp.JSON().Path("$.total").Number().Raw() > 0.0 {
						backupName := callResp.JSON().Path("$.items[0].name").String().Raw()
						l4g.Info(backupName)
						if strings.Contains(backupName, "final") && exist {
							return nil
						} else {
							return fmt.Errorf("CheckBackup failed, exist: %t, status code is: %d, http details is: %s", exist, httpInfo.RespStatusCode, httpInfo.ToString())
						}
					} else if exist {
						return fmt.Errorf("CheckBackup failed, exist: %t, http details is: %s", exist, httpInfo.ToString())
					}
					return nil

				},
				retry.DelayType(retry.FixedDelay),
				retry.Delay(time.Second*60),
				retry.Attempts(30),
				retry.OnRetry(func(n uint, err error) {
					l4g.Info("#%d: %s\n", n, err)
				}),
			)
			if err != nil {
				l4g.Error(err)
				allure.FailNow(err)
			}
		}),
	)
}
