package tui import ( "context" "fmt" "sync" "time" "dbbackup/internal/backup" "dbbackup/internal/config" "dbbackup/internal/database" "dbbackup/internal/logger" "dbbackup/internal/progress" ) // TUIProgressReporter is a progress reporter that integrates with the TUI type TUIProgressReporter struct { mu sync.RWMutex operations map[string]*progress.OperationStatus callbacks []func([]progress.OperationStatus) } // NewTUIProgressReporter creates a new TUI-compatible progress reporter func NewTUIProgressReporter() *TUIProgressReporter { return &TUIProgressReporter{ operations: make(map[string]*progress.OperationStatus), callbacks: make([]func([]progress.OperationStatus), 0), } } // AddCallback adds a callback function to be called when operations update func (t *TUIProgressReporter) AddCallback(callback func([]progress.OperationStatus)) { t.mu.Lock() defer t.mu.Unlock() t.callbacks = append(t.callbacks, callback) } // notifyCallbacks calls all registered callbacks with current operations func (t *TUIProgressReporter) notifyCallbacks() { operations := make([]progress.OperationStatus, 0, len(t.operations)) for _, op := range t.operations { operations = append(operations, *op) } for _, callback := range t.callbacks { go callback(operations) } } // StartOperation starts tracking a new operation func (t *TUIProgressReporter) StartOperation(id, name, opType string) *TUIOperationTracker { t.mu.Lock() defer t.mu.Unlock() operation := &progress.OperationStatus{ ID: id, Name: name, Type: opType, Status: "running", StartTime: time.Now(), Progress: 0, Message: fmt.Sprintf("Starting %s: %s", opType, name), Details: make(map[string]string), Steps: make([]progress.StepStatus, 0), } t.operations[id] = operation t.notifyCallbacks() return &TUIOperationTracker{ reporter: t, operationID: id, } } // TUIOperationTracker tracks progress for TUI display type TUIOperationTracker struct { reporter *TUIProgressReporter operationID string } // UpdateProgress updates the operation progress func (t *TUIOperationTracker) UpdateProgress(progress int, message string) { t.reporter.mu.Lock() defer t.reporter.mu.Unlock() if op, exists := t.reporter.operations[t.operationID]; exists { op.Progress = progress op.Message = message t.reporter.notifyCallbacks() } } // Complete marks the operation as completed func (t *TUIOperationTracker) Complete(message string) { t.reporter.mu.Lock() defer t.reporter.mu.Unlock() if op, exists := t.reporter.operations[t.operationID]; exists { now := time.Now() op.Status = "completed" op.Progress = 100 op.Message = message op.EndTime = &now op.Duration = now.Sub(op.StartTime) t.reporter.notifyCallbacks() } } // Fail marks the operation as failed func (t *TUIOperationTracker) Fail(message string) { t.reporter.mu.Lock() defer t.reporter.mu.Unlock() if op, exists := t.reporter.operations[t.operationID]; exists { now := time.Now() op.Status = "failed" op.Message = message op.EndTime = &now op.Duration = now.Sub(op.StartTime) t.reporter.notifyCallbacks() } } // GetOperations returns all current operations func (t *TUIProgressReporter) GetOperations() []progress.OperationStatus { t.mu.RLock() defer t.mu.RUnlock() operations := make([]progress.OperationStatus, 0, len(t.operations)) for _, op := range t.operations { operations = append(operations, *op) } return operations } // SilentLogger implements logger.Logger but doesn't output anything type SilentLogger struct{} func (s *SilentLogger) Info(msg string, args ...any) {} func (s *SilentLogger) Warn(msg string, args ...any) {} func (s *SilentLogger) Error(msg string, args ...any) {} func (s *SilentLogger) Debug(msg string, args ...any) {} func (s *SilentLogger) Time(msg string, args ...any) {} func (s *SilentLogger) StartOperation(name string) logger.OperationLogger { return &SilentOperation{} } // SilentOperation implements logger.OperationLogger but doesn't output anything type SilentOperation struct{} func (s *SilentOperation) Update(message string, args ...any) {} func (s *SilentOperation) Complete(message string, args ...any) {} func (s *SilentOperation) Fail(message string, args ...any) {} // SilentProgressIndicator implements progress.Indicator but doesn't output anything type SilentProgressIndicator struct{} func (s *SilentProgressIndicator) Start(message string) {} func (s *SilentProgressIndicator) Update(message string) {} func (s *SilentProgressIndicator) Complete(message string) {} func (s *SilentProgressIndicator) Fail(message string) {} func (s *SilentProgressIndicator) Stop() {} // RunBackupInTUI runs a backup operation with TUI-compatible progress reporting func RunBackupInTUI(ctx context.Context, cfg *config.Config, log logger.Logger, backupType string, databaseName string, reporter *TUIProgressReporter) error { // Create database connection db, err := database.New(cfg, &SilentLogger{}) // Use silent logger if err != nil { return fmt.Errorf("failed to create database connection: %w", err) } defer db.Close() err = db.Connect(ctx) if err != nil { return fmt.Errorf("failed to connect to database: %w", err) } // Create backup engine with silent progress indicator and logger silentProgress := &SilentProgressIndicator{} engine := backup.NewSilent(cfg, &SilentLogger{}, db, silentProgress) // Start operation tracking operationID := fmt.Sprintf("%s_%d", backupType, time.Now().Unix()) tracker := reporter.StartOperation(operationID, databaseName, backupType) // Run the appropriate backup type switch backupType { case "single": tracker.UpdateProgress(10, "Preparing single database backup...") err = engine.BackupSingle(ctx, databaseName) case "cluster": tracker.UpdateProgress(10, "Preparing cluster backup...") err = engine.BackupCluster(ctx) case "sample": tracker.UpdateProgress(10, "Preparing sample backup...") err = engine.BackupSample(ctx, databaseName) default: err = fmt.Errorf("unknown backup type: %s", backupType) } // Update final status if err != nil { tracker.Fail(fmt.Sprintf("Backup failed: %v", err)) return err } else { tracker.Complete(fmt.Sprintf("%s backup completed successfully", backupType)) return nil } }