package crrf

import (
	"fmt"
	"time"

	. "github.com/tidbcloud/dataflow-service-e2e/cases"
	"github.com/tidbcloud/dataflow-service-e2e/cases/dedicated/common"
	"github.com/tidbcloud/dataflow-service-e2e/internal/config"
	. "github.com/tidbcloud/dataflow-service-e2e/internal/model"
)

// crrfAutoDRTriggerCase tests the Auto-CRRF DR-trigger path:
// Enable DR with S1 → CRRF auto-enables → disable DR → CRRF auto-disables →
// re-enable DR with S1 → CRRF auto-enables → disable DR → CRRF auto-disables →
// enable DR with S2 → snapshot → CRRF auto-enables with S2.
type crrfAutoDRTriggerCase struct {
	BaseCase
	backupValidator *common.BackupValidator
	crrfValidator   *common.CRRFValidator
	cloudProvider   CloudProviderType
}

func NewCRRFAutoDRTriggerCase(cloudProvider CloudProviderType) E2ECase {
	return &crrfAutoDRTriggerCase{cloudProvider: cloudProvider}
}

func (m *crrfAutoDRTriggerCase) Name() string {
	return fmt.Sprintf("%sCRRFAutoDRTrigger", m.cloudProvider)
}

func (m *crrfAutoDRTriggerCase) Tags() map[string]string {
	return map[string]string{TagKeyDomain: TagValueDomainCRRF}
}

func (m *crrfAutoDRTriggerCase) DeclareDependencyRequirements() map[string]DependencyRequirement {
	return map[string]DependencyRequirement{
		"tidb": &TiDBDedicatedRequirement{
			Dedicated: true,
			TiDBDedicatedDimensions: TiDBDedicatedDimensions{
				CloudProvider: m.cloudProvider,
				Region:        config.GlobalConfig.DefaultRegionOf(m.cloudProvider),
			},
		},
	}
}

func (m *crrfAutoDRTriggerCase) Run() {
	tidbCluster := m.TiDBDedicated("tidb")

	m.backupValidator = common.NewBackupValidator(m.Logger())
	m.crrfValidator = common.NewCRRFValidator(m.Logger())

	// CRRF is AWS-only
	if m.cloudProvider != AWS {
		m.Logger().Info("skipping Auto-CRRF DR trigger test for non-AWS provider %s", m.cloudProvider)
		return
	}

	var (
		secondaryRegionS1     string
		originalStateCaptured bool
		createdBackupIDs      []uint64
	)

	// Cleanup: delete backups created during test
	defer func() {
		if !originalStateCaptured {
			return
		}
		m.Logger().Info("cleanup: deleting %d backups created during test", len(createdBackupIDs))
		for _, backupID := range createdBackupIDs {
			if err := m.backupValidator.Client().DeleteBackup(tidbCluster, backupID); err != nil {
				m.Logger().Warn("cleanup: failed to delete backup %d: %v", backupID, err)
			} else {
				m.Logger().Info("cleanup: deleted backup %d", backupID)
			}
		}
		m.Logger().Info("cleanup: completed, cluster will be deleted by test framework")
	}()

	// Phase 1: Setup
	m.Step("capture original backup setting", func() error {
		_, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err != nil {
			return err
		}
		originalStateCaptured = true
		return nil
	})

	m.
		Step("enable PiTR", func() error {
			return m.backupValidator.EnablePiTR(tidbCluster)
		}).
		Step("trigger snapshot backup to activate log backup", func() error {
			backupID, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-dr-setup"),
				30*time.Minute,
			)
			if err == nil && backupID > 0 {
				createdBackupIDs = append(createdBackupIDs, backupID)
			}
			return err
		}).
		Step("wait backup setting ready after snapshot backup", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		})

	// Phase 2: First DR cycle (secondary region S1)
	m.
		Step("resolve secondary region S1", func() error {
			var err error
			secondaryRegionS1, err = m.backupValidator.GetSecondaryRegion(tidbCluster, false)
			return err
		}).
		Step("enable dual-region backup with S1", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, secondaryRegionS1)
		}).
		Step("wait for dual-region enable to settle", func() error {
			time.Sleep(30 * time.Second)
			return nil
		}).
		Step("trigger snapshot backup to restart PiTR after dual-region enable", func() error {
			backupID, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-dr-s1"),
				30*time.Minute,
			)
			if err == nil && backupID > 0 {
				createdBackupIDs = append(createdBackupIDs, backupID)
			}
			return err
		}).
		Step("wait backup setting ready after snapshot backup", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF enabled after dual-region with S1", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second).
		Step("disable dual-region backup", func() error {
			return m.backupValidator.DisableDualRegion(tidbCluster)
		}).
		Step("wait for dual-region disable to settle", func() error {
			time.Sleep(30 * time.Second)
			return nil
		}).
		Step("wait backup setting 'Waiting_For_Editing_PiTR' after dual-region disable", func() error {
			return m.backupValidator.WaitForBackupSettingStatus(tidbCluster, "Waiting_For_Editing_PiTR", 5*time.Minute)
		}).
		Expect("CRRF disabled after dual-region disable", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "disabled")
		}, 30, 20*time.Second)

	// Phase 3: Second DR cycle (same secondary region S1)
	m.
		Step("re-enable dual-region backup with S1", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, secondaryRegionS1)
		}).
		Step("wait for re-enable dual-region to settle", func() error {
			time.Sleep(30 * time.Second)
			return nil
		}).
		Step("wait backup setting ready after re-enable snapshot backup", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF enabled after re-enable dual-region with S1", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second).
		Step("disable dual-region backup again", func() error {
			return m.backupValidator.DisableDualRegion(tidbCluster)
		}).
		Step("wait for second dual-region disable to settle", func() error {
			time.Sleep(30 * time.Second)
			return nil
		}).
		Step("wait backup setting 'Waiting_For_Editing_PiTR' after dual-region disable", func() error {
			return m.backupValidator.WaitForBackupSettingStatus(tidbCluster, "Waiting_For_Editing_PiTR", 5*time.Minute)
		}).
		Expect("CRRF disabled after second dual-region disable", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "disabled")
		}, 30, 20*time.Second)

	// Phase 4: DR with different secondary region S2
	differentRegion, err := m.backupValidator.GetDifferentSecondaryRegion(tidbCluster, secondaryRegionS1)
	if err != nil {
		m.Logger().Info("skipping different-secondary-region test: only 1 secondary region available (%v)", err)
		return
	}

	m.
		Step("enable dual-region backup with S2 (different region)", func() error {
			return m.backupValidator.EnableDualRegion(tidbCluster, differentRegion)
		}).
		Step("wait for S2 dual-region enable to settle", func() error {
			time.Sleep(30 * time.Second)
			return nil
		}).
		Step("trigger snapshot backup to restart PiTR after S2 enable", func() error {
			backupID, err := m.backupValidator.TriggerSnapshotBackupAndWait(
				tidbCluster,
				GenerateClusterName("crrf-dr-s2"),
				30*time.Minute,
			)
			if err == nil && backupID > 0 {
				createdBackupIDs = append(createdBackupIDs, backupID)
			}
			return err
		}).
		Step("wait backup setting ready after S2 snapshot backup", func() error {
			return m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute)
		}).
		Expect("CRRF auto enabled (DR trigger, S2)", func() error {
			return m.backupValidator.ExpectCRRFStatus(tidbCluster, "enabled")
		}, 30, 30*time.Second)
}
