package backup

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"
)

// pitrBackupSmokeCase tests PiTR (Point-in-Time Recovery) backup functionality.
// It covers:
// - Enable PiTR on a cluster with existing snapshot backup
// - Verify log backups are running
// - Verify PiTR time ranges are available
// - Disable PiTR
type pitrBackupSmokeCase struct {
	BaseCase
	backupValidator *common.BackupValidator
	cloudProvider   CloudProviderType
}

// NewPiTRBackupSmokeCase creates a new PiTR backup smoke test case.
func NewPiTRBackupSmokeCase(cloudProvider CloudProviderType) E2ECase {
	return &pitrBackupSmokeCase{cloudProvider: cloudProvider}
}

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

func (m *pitrBackupSmokeCase) Tags() map[string]string {
	return map[string]string{TagKeyDomain: TagValueDomainBR}
}

func (m *pitrBackupSmokeCase) 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 *pitrBackupSmokeCase) Run() {
	const (
		backupTimeout        = 20 * time.Minute
		pitrTimeRangeTimeout = 30 * 2 * time.Minute // 30 attempts × 2 minutes
		defaultScheduleTime  = "03:00"
		retentionDays        = 7
	)

	tidbCluster := m.TiDBDedicated("tidb")

	// Initialize validator
	m.backupValidator = common.NewBackupValidator(m.Logger())

	// Store original PiTR setting for cleanup
	var originalPiTREnabled bool

	// Query and store original settings
	m.Step("query original PiTR settings", func() error {
		resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err != nil {
			return err
		}
		originalPiTREnabled = resp.ContinuousBackupSchedule != nil && resp.ContinuousBackupSchedule.Enable
		m.Logger().Info("original PiTR enabled: %v", originalPiTREnabled)
		return nil
	})

	// Cleanup: restore original settings
	defer func() {
		m.Step("restore original PiTR settings", func() error {
			if originalPiTREnabled {
				return m.backupValidator.EnablePiTR(tidbCluster)
			}
			return m.backupValidator.DisablePiTR(tidbCluster)
		})
	}()

	m.
		// Step 1: Ensure auto backup is enabled (PiTR requires snapshot backup base)
		Step("ensure auto backup is enabled", func() error {
			err := m.backupValidator.IsAutoBackupEnabled(tidbCluster)
			if err != nil {
				m.Logger().Info("auto backup not enabled, enabling daily backup")
				return m.backupValidator.EnableDailyAutoBackup(tidbCluster, defaultScheduleTime, retentionDays)
			}
			return nil
		}).

		// Step 2: Enable PiTR and trigger activation backup
		Step("ensure PiTR is enabled and activated", func() error {
			err := m.backupValidator.IsPiTREnabled(tidbCluster)
			if err != nil {
				m.Logger().Info("PiTR not enabled, enabling it")
				if enableErr := m.backupValidator.EnablePiTR(tidbCluster); enableErr != nil {
					return enableErr
				}

				m.Logger().Info("triggering snapshot backup to activate PiTR")
				backupName := GenerateBackupName("pitr-init-backup")
				_, err := m.backupValidator.TriggerSnapshotBackupAndWait(tidbCluster, backupName, backupTimeout)
				return err
			}
			return nil
		}).

		// Step 3: List log backups
		Step("list log backups", func() error {
			resp, err := m.backupValidator.Client().ListLogBackups(tidbCluster)
			if err != nil {
				m.Logger().Warn("failed to list log backups: %s", err.Error())
				// This might fail initially, not critical
				return nil
			}
			m.Logger().Info("log backups count: %d", len(resp.Items))
			for _, item := range resp.Items {
				m.Logger().Info("  log backup: id=%d, status=%s", item.Id, item.Status)
			}
			return nil
		}).

		// Step 4: Wait for PiTR time ranges to become available
		Expect("PiTR time ranges are available", func() error {
			return m.backupValidator.HasPiTRTimeRanges(tidbCluster)
		}, 30, 2*time.Minute). // May take time for log backups to accumulate

		// Step 5: Verify log backup is running
		Step("verify log backup details", func() error {
			resp, err := m.backupValidator.Client().ListLogBackups(tidbCluster)
			if err != nil {
				return err
			}
			if len(resp.Items) == 0 {
				return fmt.Errorf("no log backups found")
			}
			// Check at least one log backup is in running or success state
			for _, item := range resp.Items {
				m.Logger().Info("log backup %d status: %s", item.Id, item.Status)
			}
			return nil
		}).

		// Step 6: Disable PiTR
		RetryDo("disable PiTR", func() error {
			return m.backupValidator.DisablePiTR(tidbCluster)
		}, 3, time.Minute).

		// Step 7: Verify PiTR is disabled
		Expect("PiTR is disabled", func() error {
			return m.backupValidator.IsPiTRDisabled(tidbCluster)
		}, 5, time.Minute)
}
