package backup

import (
	"context"
	"time"

	"github.com/go-co-op/gocron"
	"github.com/tidbcloud/dataflow-service/app/worker/common"
	"github.com/tidbcloud/dataflow-service/internal/config"
)

const (
	syncServerlessBackupProgressIntervalMinutes       = 15
	syncServerlessRestoreProgressIntervalSeconds      = 5
	deleteServerlessExpiredAutoBackupsIntervalMinutes = 30
	serverlessScheduleAutoBackupIntervalMinutes       = 5
	manageServerlessTablePartitionsIntervalDays       = 1
	manageServerlessTablePartitionsDayTime            = "09:00"

	deleteExpiredAutoBackupsIntervalMinutes  = 30
	scheduleAutoBackupIntervalSeconds        = 10
	leakCheckIntervalMinutes                 = 60
	classicRestoreAccessCleanupIntervalHours = 1

	syncLogBackupFromInfraIntervalSeconds        = 30
	autoBackupScheduleHealthCheckIntervalMinutes = 30
	// reconcileStuckRunningBackupsIntervalMinutes drives the worker that fixes
	// up backups stuck in `running` after the create DAG has aborted, so the
	// downstream `clean_failed_snapshot_backup_storage` step can run and
	// orphan S3 data is collected without a multi-day wait for retention.
	reconcileStuckRunningBackupsIntervalMinutes = 30
	// markBackupsFailedFromFailedDagIntervalMinutes drives the worker that
	// catches legacy backups whose create DAG already failed before the
	// BeforeFail hook was deployed. Cheap (local fastflow query only), so we
	// run it more frequently than the dataplane reconciler.
	markBackupsFailedFromFailedDagIntervalMinutes = 10
	// markBackupExportsFailedFromFailedDagIntervalMinutes drives the worker that
	// fixes backup exports stuck in `running`/`pending` after their create DAG
	// was watchdog-killed. Same cheap local fastflow query as the backup variant.
	markBackupExportsFailedFromFailedDagIntervalMinutes = 10
)

func NewWorkerScheduler(ctx context.Context, loc *time.Location) (*gocron.Scheduler, error) {
	s := gocron.NewScheduler(loc)
	s.SingletonModeAll()

	if _, err := s.Every(scheduleAutoBackupIntervalSeconds).Seconds().
		Do(common.WorkerFunWrapper("ScheduleAutoBackup", ScheduleAutoBackup)); err != nil {
		return nil, err
	}
	if _, err := s.Every(deleteExpiredAutoBackupsIntervalMinutes).Minutes().
		Do(common.WorkerFunWrapper("DeleteExpiredAutoBackups", DeleteExpiredAutoBackups)); err != nil {
		return nil, err
	}
	if _, err := s.Every(syncLogBackupFromInfraIntervalSeconds).Seconds().
		Do(common.WorkerFunWrapper("SyncLogBackupFromInfra", SyncLogBackupFromInfra)); err != nil {
		return nil, err
	}
	if _, err := s.Every(autoBackupScheduleHealthCheckIntervalMinutes).Minutes().
		Do(common.WorkerFunWrapper(
			"AutoBackupScheduleHealthCheck", AutoBackupScheduleHealthCheck)); err != nil {
		return nil, err
	}
	if _, err := s.Every(reconcileStuckRunningBackupsIntervalMinutes).Minutes().
		Do(common.WorkerFunWrapper(
			"ReconcileStuckRunningBackups", ReconcileStuckRunningBackups)); err != nil {
		return nil, err
	}
	if _, err := s.Every(markBackupsFailedFromFailedDagIntervalMinutes).Minutes().
		Do(common.WorkerFunWrapper(
			"MarkBackupsFailedFromFailedDag", MarkBackupsFailedFromFailedDag)); err != nil {
		return nil, err
	}
	if _, err := s.Every(markBackupExportsFailedFromFailedDagIntervalMinutes).Minutes().
		Do(common.WorkerFunWrapper(
			"MarkBackupExportsFailedFromFailedDag", MarkBackupExportsFailedFromFailedDag)); err != nil {
		return nil, err
	}
	if _, err := s.Every(classicRestoreAccessCleanupIntervalHours).Hours().
		Do(common.WorkerFunWrapper("CleanupExpiredClassicRestoreAccess", CleanupExpiredClassicRestoreAccess)); err != nil {
		return nil, err
	}
	if _, err := s.Cron("0 3 * * *").
		Do(common.WorkerFunWrapper("SnapshotBackupCleanUpCheck", SnapshotBackupCleanUpCheck)); err != nil {
		return nil, err
	}
	if _, err := s.Cron("0 4 * * *").
		Do(common.WorkerFunWrapper("BackupNodeGroupAndNodePoolExpiredCheck",
			BackupNodeGroupAndNodePoolExpiredCheck)); err != nil {
		return nil, err
	}

	// schedule log backup size worker at 10min of every hour
	if _, err := s.Cron("10 * * * *").
		Do(common.WorkerFunWrapper("CalculateLogBackupSize", CalculateLogBackupSize)); err != nil {
		return nil, err
	}
	if _, err := s.Cron("30 * * * *").
		Do(common.WorkerFunWrapper("GenerateBackupBillingRecord", GenerateBackupBillingRecord)); err != nil {
		return nil, err
	}
	if _, err := s.Cron("30 * * * *").
		Do(common.WorkerFunWrapper("GenerateLogBackupBillingRecord", GenerateLogBackupBillingRecord)); err != nil {
		return nil, err
	}
	// Register CompactLogBackup worker (enabled by default)
	// Note: This uses the global interval setting for the scheduler.
	CompactBackupInterval, err := config.Global().CompactConfig.GetCompactBackupIntervalDuration()
	if err != nil {
		return nil, err
	}
	if _, err := s.Every(CompactBackupInterval).
		Do(common.WorkerFunWrapper("CompactLogBackup", CompactLogBackup)); err != nil {
		return nil, err
	}

	return s, nil
}
