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"
	"github.com/tidbcloud/dataflow-service-e2e/internal/tidbcloud/dedicated/consoleapi"
	br_model "github.com/tidbcloud/dataflow-service-e2e/internal/tidbcloud/dedicated/consoleapi/br/model"
)

// backupSettingSmokeCase tests backup settings configuration for a TiDB Dedicated cluster.
// It covers:
// - Query default backup settings
// - Modify auto backup schedule (Daily/Weekly)
// - Enable/disable PiTR
// - Modify retention period
type backupSettingSmokeCase struct {
	BaseCase
	backupValidator *common.BackupValidator
	cloudProvider   CloudProviderType
}

// NewBackupSettingSmokeCase creates a new backup settings smoke test case.
func NewBackupSettingSmokeCase(cloudProvider CloudProviderType) E2ECase {
	return &backupSettingSmokeCase{cloudProvider: cloudProvider}
}

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

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

func (m *backupSettingSmokeCase) 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 *backupSettingSmokeCase) Run() {
	const (
		backupTimeout        = 20 * time.Minute
		defaultScheduleTime  = "03:00"
		defaultRetentionDays = 7
		weeklyScheduleTime   = "04:00"
		weeklyRetentionDays  = 14
	)

	tidbCluster := m.TiDBDedicated("tidb")

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

	// Store original settings for cleanup
	var originalScheduleType string
	var originalDailyScheduleTime string
	var originalRetentionPeriod int64
	var originalPiTREnabled bool

	// Query and store original settings
	m.Step("query original backup settings", func() error {
		resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		if err != nil {
			return err
		}
		if resp.SnapshotBackupSchedule != nil {
			originalScheduleType = resp.SnapshotBackupSchedule.ScheduleType
			originalDailyScheduleTime = resp.SnapshotBackupSchedule.DailyScheduleTime
			if resp.SnapshotBackupSchedule.RetentionPolicy != nil {
				originalRetentionPeriod = resp.SnapshotBackupSchedule.RetentionPolicy.RetentionPeriod
			}
		}
		originalPiTREnabled = resp.ContinuousBackupSchedule != nil && resp.ContinuousBackupSchedule.Enable
		m.Logger().Info("original settings: scheduleType=%s, dailyTime=%s, retention=%d, pitrEnabled=%v",
			originalScheduleType, originalDailyScheduleTime, originalRetentionPeriod, originalPiTREnabled)
		return nil
	})

	// Cleanup: restore original settings
	defer func() {
		m.Step("restore original backup settings", func() error {
			// Wait for backup setting to be ready before modifying
			if err := m.backupValidator.WaitForBackupSettingReady(tidbCluster, 5*time.Minute); err != nil {
				m.Logger().Warn("backup setting not ready before cleanup: %v", err)
				// Continue anyway, modification might still work
			}

			req := &consoleapi.ModifyBackupSettingRequest{}

			if originalScheduleType != "" {
				req.SnapshotBackupSchedule = &consoleapi.SnapshotBackupScheduleRequest{
					Disable:           false,
					ScheduleType:      originalScheduleType,
					DailyScheduleTime: originalDailyScheduleTime,
				}
				if originalRetentionPeriod > 0 {
					req.SnapshotBackupSchedule.RetentionPolicy = &consoleapi.RetentionPolicyReq{
						RetentionPeriod: originalRetentionPeriod,
					}
				}
			} else {
				req.SnapshotBackupSchedule = &consoleapi.SnapshotBackupScheduleRequest{
					Disable: true,
				}
			}

			req.ContinuousBackupSchedule = &consoleapi.ContinuousBackupScheduleRequest{
				Enable: originalPiTREnabled,
			}
			return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, req)
		})
	}()

	m.
		// Test 1: Query backup settings
		Step("query backup settings", func() error {
			resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
			if err != nil {
				return err
			}
			m.Logger().Info("backup settings: status=%s, actions=%v",
				resp.Status, resp.Actions)
			return nil
		}).

		// Test 2: Enable daily auto backup
		RetryDo("enable daily auto backup", func() error {
			return m.backupValidator.EnableDailyAutoBackup(tidbCluster, defaultScheduleTime, defaultRetentionDays)
		}, 3, time.Minute).

		// Verify daily auto backup is enabled
		Expect("daily auto backup is enabled", func() error {
			return m.backupValidator.IsAutoBackupEnabled(tidbCluster)
		}, 5, time.Second*30).

		// Test 3: Modify to weekly backup
		RetryDo("modify to weekly auto backup", func() error {
			req := &consoleapi.ModifyBackupSettingRequest{
				SnapshotBackupSchedule: &consoleapi.SnapshotBackupScheduleRequest{
					Disable:      false,
					ScheduleType: string(br_model.DataflowSnapshotBackupScheduleTypeWeekly),
					WeeklySchedule: &consoleapi.WeeklyScheduleRequest{
						Days: []string{
							string(br_model.DataflowWeekDayMONDAY),
							string(br_model.DataflowWeekDayWEDNESDAY),
							string(br_model.DataflowWeekDayFRIDAY),
						},
						ScheduleTime: weeklyScheduleTime,
					},
					RetentionPolicy: &consoleapi.RetentionPolicyReq{
						RetentionPeriod: weeklyRetentionDays,
					},
				},
			}
			return m.backupValidator.Client().ModifyBackupSetting(tidbCluster, req)
		}, 3, time.Minute).

		// Verify weekly schedule is set
		Expect("weekly backup schedule is configured", func() error {
			resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
			if err != nil {
				return err
			}
			if resp.SnapshotBackupSchedule == nil {
				return fmt.Errorf("snapshot backup schedule is nil")
			}
			if resp.SnapshotBackupSchedule.ScheduleType != string(br_model.DataflowSnapshotBackupScheduleTypeWeekly) {
				return fmt.Errorf("expected Weekly schedule, got %s",
					resp.SnapshotBackupSchedule.ScheduleType)
			}
			return nil
		}, 5, time.Second*30).

		// Test 4: Enable PiTR
		RetryDo("enable PiTR", func() error {
			return m.backupValidator.EnablePiTR(tidbCluster)
		}, 3, time.Minute).

		// // Wait for backup setting to be ready (status should be Normal or Editing_Snapshot)
		// Expect("backup setting ready for operations", func() error {
		// 	resp, err := m.backupValidator.Client().QueryBackupSetting(tidbCluster)
		// 	if err != nil {
		// 		return err
		// 	}
		// 	// Acceptable statuses: Normal, Editing_Snapshot
		// 	// Unacceptable: Waiting_For_Editing_PiTR, Editing_PiTR
		// 	if resp.Status == "Waiting_For_Editing_PiTR" || resp.Status == "Editing_PiTR" {
		// 		return fmt.Errorf("backup setting not ready, status: %s", resp.Status)
		// 	}
		// 	m.Logger().Info("backup setting is ready, status: %s", resp.Status)
		// 	return nil
		// }, 10, time.Minute).

		// Trigger a snapshot backup to activate PiTR
		// PiTR requires at least one snapshot backup to become effective
		Step("trigger snapshot backup for PiTR activation", func() error {
			backupName := GenerateBackupName("pitr-init-backup")
			_, err := m.backupValidator.TriggerSnapshotBackupAndWait(tidbCluster, backupName, backupTimeout)
			return err
		}).

		// Verify PiTR is enabled
		Expect("PiTR is enabled", func() error {
			return m.backupValidator.IsPiTREnabled(tidbCluster)
		}, 10, time.Second*30). // PiTR may take time to initialize

		// Test 5: Disable PiTR
		RetryDo("disable PiTR", func() error {
			return m.backupValidator.DisablePiTR(tidbCluster)
		}, 3, time.Second*30).

		// Verify PiTR is disabled
		Expect("PiTR is disabled", func() error {
			return m.backupValidator.IsPiTRDisabled(tidbCluster)
		}, 5, time.Second*30).

		// Test 6: Disable auto backup
		RetryDo("disable auto backup", func() error {
			return m.backupValidator.DisableAutoBackup(tidbCluster)
		}, 3, time.Second*30).

		// Verify auto backup is disabled
		Expect("auto backup is disabled", func() error {
			return m.backupValidator.IsAutoBackupDisabled(tidbCluster)
		}, 5, time.Second*30)
}
