using Microsoft.Extensions.Configuration; namespace URLNotesGrabberCORE { // Inverse of OutputMode. For each Blog with a TTFolderPath, restores every *.bak back // to its *.txt, first preserving the current *.txt as the next-free *.bkN. Consumes // the *.bak (File.Move). Filesystem-only; reads the DB only to enumerate folders. public static class RevertMode { public static int Run(IConfiguration config, string? blogFilter = null) { DataAccess.EnsureTTFileHelperColumnsExist(); var blogs = DataAccess.GetAllBlogsWithTTFolderPath(); if (!string.IsNullOrWhiteSpace(blogFilter)) blogs = blogs.Where(b => string.Equals(b.BlogName, blogFilter, StringComparison.OrdinalIgnoreCase)).ToList(); Console.WriteLine($"Found {blogs.Count} blog(s) to process."); // Count total *.bak files up front so the confirmation prompt is meaningful. int totalBakFiles = 0; foreach (var (_, ttFolderPath) in blogs) { if (string.IsNullOrWhiteSpace(ttFolderPath) || !Directory.Exists(ttFolderPath)) continue; try { totalBakFiles += Directory.GetFiles(ttFolderPath, "*.bak").Length; } catch { } } if (totalBakFiles == 0) { Console.WriteLine("No .bak files found in any blog folder. Nothing to revert."); return 0; } Console.Write($"WARNING: This will restore {totalBakFiles} .bak file(s) over their .txt files across {blogs.Count} blog folder(s). " + $"Current .txt files are preserved as the next-free .bkN. Continue? (yes/no): "); string? response = Console.ReadLine(); if (string.IsNullOrWhiteSpace(response) || !response.Equals("yes", StringComparison.OrdinalIgnoreCase)) { Console.WriteLine("Operation cancelled."); return 0; } foreach (var (blogName, ttFolderPath) in blogs) { Console.WriteLine($"\nProcessing blog: {blogName}"); if (string.IsNullOrWhiteSpace(ttFolderPath) || !Directory.Exists(ttFolderPath)) { Console.WriteLine($" TTFolderPath does not exist or is not set. Skipping."); continue; } Console.WriteLine($" TTFolderPath: {ttFolderPath}"); var (restored, backedUp) = RevertFolder(ttFolderPath); Console.WriteLine($" Restored {restored} file(s); backed up {backedUp} current .txt file(s)."); } Console.WriteLine("\nRevert mode complete."); return 0; } // For each *.bak: back up the current *.txt to the next-free *.bkN, then File.Move(bak -> txt). private static (int restored, int backedUp) RevertFolder(string folderPath) { int restored = 0, backedUp = 0; var bakFiles = Directory.GetFiles(folderPath, "*.bak"); if (bakFiles.Length == 0) { Console.WriteLine(" Nothing to revert (no .bak files)."); return (restored, backedUp); } foreach (var bakFile in bakFiles) { try { string txtPath = Path.ChangeExtension(bakFile, ".txt"); if (File.Exists(txtPath)) { string bkPath = NextFreeBkPath(txtPath); File.Move(txtPath, bkPath); backedUp++; Console.WriteLine($" Backed up {Path.GetFileName(txtPath)} -> {Path.GetFileName(bkPath)}"); } File.Move(bakFile, txtPath); restored++; Console.WriteLine($" Restored {Path.GetFileName(bakFile)} -> {Path.GetFileName(txtPath)}"); } catch (Exception ex) { Console.WriteLine($" Error reverting {Path.GetFileName(bakFile)}: {ex.Message}"); } } return (restored, backedUp); } // Returns the lowest unused .bkN path for a given .txt file (.bk1, .bk2, ...). private static string NextFreeBkPath(string txtFile) { for (int n = 1; ; n++) { string candidate = Path.ChangeExtension(txtFile, $".bk{n}"); if (!File.Exists(candidate)) return candidate; } } } }