package backup

import (
	"fmt"
	"time"

	retry "github.com/avast/retry-go/v4"
	"github.com/tidbcloud/dataflow-service-e2e/cases"
	"github.com/tidbcloud/dataflow-service-e2e/cases/essential/common"
	"github.com/tidbcloud/dataflow-service-e2e/internal/config"
	. "github.com/tidbcloud/dataflow-service-e2e/internal/model"
	"github.com/tidbcloud/dataflow-service-e2e/internal/tidbcloud/essential"
)

type PiTRRestoreSmokeCase struct {
	cases.BaseCase
	provider CloudProviderType
}

func NewPiTRRestoreSmokeCase(provider CloudProviderType) cases.E2ECase {
	return &PiTRRestoreSmokeCase{provider: provider}
}
func (c *PiTRRestoreSmokeCase) Name() string {
	return fmt.Sprintf("%sEssentialV1PiTRRestoreSmoke", c.provider)
}
func (c *PiTRRestoreSmokeCase) Tags() map[string]string {
	return map[string]string{cases.TagKeyDomain: config.DomainBR, cases.TagKeyAPIModes: common.APIConsoleOnly}
}
func (c *PiTRRestoreSmokeCase) DeclareDependencyRequirements() map[string]DependencyRequirement {
	region := config.GlobalConfig.DefaultRegionOf(c.provider)
	return map[string]DependencyRequirement{
		"essential": &TiDBServerlessRequirement{
			Dedicated: true,
			TiDBServerlessDimensions: TiDBServerlessDimensions{
				CloudProvider: c.provider,
				Region:        region,
				ServicePlan:   essential.ServicePlanEssential,
			},
		},
	}
}

func (c *PiTRRestoreSmokeCase) Run() {
	source := c.TiDBServerless("essential")
	c.Step("verify Essential v1 cluster", func() error { return common.ValidateCluster(source) })
	ctx, br := c.Context(), essential.NewBRClient()
	baseline, marker := c.Name()+"-baseline", c.Name()+"-marker"
	restoreName := c.Name() + "-" + common.UniqueRunSuffix()
	var point time.Time
	var targetID string
	defer func() { cleanupRestoreTarget(&c.BaseCase, source, targetID) }()
	c.Step("wait for PiTR restore window", func() error {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-time.After(70 * time.Second):
			return nil
		}
	})
	c.Step("write PiTR restore baseline", func() error {
		ts, err := common.WriteMarker(source, baseline)
		point = time.Unix(ts, 0).UTC()
		return err
	})
	c.Step("wait for a later PiTR second", func() error {
		delay := time.Until(point.Add(2 * time.Second))
		if delay > 0 {
			select {
			case <-ctx.Done():
				return ctx.Err()
			case <-time.After(delay):
			}
		}
		return nil
	})
	c.Step("write PiTR restore marker", func() error { _, err := common.WriteMarker(source, marker); return err })
	c.Step("submit PiTR restore", func() error {
		return retry.Do(func() error {
			var err error
			targetID, err = br.Restore(ctx, source, "", restoreName, "PiTRRestore", point)
			if err == nil {
				return nil
			}
			if essential.IsConsoleBusinessErrorCode(err, "61204007") {
				return err
			}
			return classifyRestoreRetry(err)
		}, retry.Attempts(18), retry.Delay(10*time.Second), retry.DelayType(retry.FixedDelay), retry.Context(ctx))
	})
	c.Expect("PiTR restore completes", func() error {
		state, err := br.RestoreState(ctx, source, targetID)
		if err != nil {
			return err
		}
		return common.ValidateRestoreState(state)
	}, 120, 30*time.Second)
	c.Expect("PiTR restore target is active", func() error { return waitRestoreTargetActive(&c.BaseCase, source, targetID) }, 60, 30*time.Second)
	var password string
	c.Step("generate PiTR restore password", func() error { var err error; password, err = common.GenerateRestorePassword(); return err })
	c.RetryDo("set PiTR restore target password", func() error { return setRestoreTargetPassword(&c.BaseCase, source, targetID, password) }, 5, 15*time.Second)
	c.Step("verify PiTR restored baseline and absent marker", func() error {
		target, err := restoreTargetEntity(&c.BaseCase, source, targetID, password)
		if err != nil {
			return err
		}
		return common.VerifyRestoreData(target, baseline, marker)
	})
}
