package steps

import (
	"context"
	"fmt"
	"time"

	apiresouce "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/resource"
)

type ownerSwitch struct {
	target   *apiresouce.ResourceHandle
	duration time.Duration
}

func NewSwitchOwner(target *apiresouce.ResourceHandle, duration time.Duration) *ownerSwitch {
	return &ownerSwitch{
		target:   target,
		duration: duration,
	}
}

func (s *ownerSwitch) Execute(ctx context.Context) error {
	return NewChaosTask(
		s.target,
		resource.TiDBClusterSelectorOpts{
			TiDBClusterCDCSelectorSpec: &resource.TiDBClusterCDCSelectorSpec{
				IsOwner: &[]bool{true, false}[0],
			},
		},
		&resource.FailureExperimentSpec{},
		s.duration).Execute(ctx)
}

func (s *ownerSwitch) String() string {
	return fmt.Sprintf("ownerSwitch: duration=%+v", s.duration)
}

type allCaptureRestart struct {
	target   *apiresouce.ResourceHandle
	duration time.Duration
}

func NewAllCaptureRestart(target *apiresouce.ResourceHandle, duration time.Duration) *allCaptureRestart {
	return &allCaptureRestart{
		target:   target,
		duration: duration,
	}
}

func (s *allCaptureRestart) Execute(ctx context.Context) error {
	return NewChaosTask(
		s.target,
		resource.TiDBClusterSelectorOpts{
			TiDBClusterCDCSelectorSpec: &resource.TiDBClusterCDCSelectorSpec{
				All: true,
			},
		},
		&resource.FailureExperimentSpec{},
		s.duration,
	).Execute(ctx)
}

func (s *allCaptureRestart) String() string {
	return fmt.Sprintf("allCaptureRestart: duration=%+v", s.duration)
}

type randomCDCFailure struct {
	target   *apiresouce.ResourceHandle
	duration time.Duration
}

func NewRandomCDCFailure(target *apiresouce.ResourceHandle, duration time.Duration) *randomCDCFailure {
	return &randomCDCFailure{
		target:   target,
		duration: duration,
	}
}

func (b *Builder) RandomCDCFailure(duration time.Duration) *Builder {
	b.steps = append(b.steps, &randomCDCFailure{
		duration: duration,
	})
	return b
}

func (s *randomCDCFailure) Execute(ctx context.Context) error {
	return NewChaosTask(
		s.target,
		resource.TiDBClusterSelectorOpts{
			TiDBClusterCDCSelectorSpec: &resource.TiDBClusterCDCSelectorSpec{
				RandomSameOne: true,
			},
		},
		&resource.FailureExperimentSpec{},
		s.duration,
	).Execute(ctx)
}

func (s *randomCDCFailure) String() string {
	return fmt.Sprintf("randomCDCFailure: duration=%+v", s.duration)
}

type randomCDCNetworkLatency struct {
	target   *apiresouce.ResourceHandle
	duration time.Duration
	latency  string
	jitter   string
}

func NewCDCRandomNetworkLatency(target *apiresouce.ResourceHandle, duration time.Duration, latency, jitter string) *randomCDCNetworkLatency {
	return &randomCDCNetworkLatency{
		target:   target,
		duration: duration,
		latency:  latency,
		jitter:   jitter,
	}
}

func (s *randomCDCNetworkLatency) Execute(ctx context.Context) error {
	selectOption := resource.TiDBClusterSelectorOpts{
		TiDBClusterCDCSelectorSpec: &resource.TiDBClusterCDCSelectorSpec{
			RandomSameOne: true,
		},
	}
	return newNetworkLatencyChaosTask(s.target, selectOption,
		s.duration, s.latency, s.jitter).Execute(ctx)
}

func (s *randomCDCNetworkLatency) String() string {
	return fmt.Sprintf("randomCDCNetworkLatency: duration=%+v, latency=%+v, jitter=%+v",
		s.duration, s.latency, s.jitter)
}

type randomCDCNetworkLoss struct {
	target   *apiresouce.ResourceHandle
	duration time.Duration
}

func NewCDCRandomNetworkLoss(target *apiresouce.ResourceHandle, duration time.Duration) *randomCDCNetworkLoss {
	return &randomCDCNetworkLoss{
		target:   target,
		duration: duration,
	}
}

func (s *randomCDCNetworkLoss) String() string {
	return fmt.Sprintf("randomCDCNetworkLoss: duration=%+v", s.duration)
}

func (s *randomCDCNetworkLoss) Execute(ctx context.Context) error {
	selectOption := resource.TiDBClusterSelectorOpts{
		TiDBClusterCDCSelectorSpec: &resource.TiDBClusterCDCSelectorSpec{
			RandomSameOne: true,
		},
	}
	return newNetworkLossChaosTask(s.target, selectOption, s.duration).Execute(ctx)
}
