using Microsoft.Extensions.Configuration; namespace URLNotesGrabberCORE { // Inverse of OutputMode. Recursively walks the PathInput tree (the same directory tree the // no-parameter run uses) and restores every *.bak back to its *.txt, first preserving the // current *.txt as the next-free *.bkN. Consumes the *.bak (File.Move). Filesystem-only; // does not read the DB. An optional blogname argument filters by path substring. public static class RevertMode { public static int Run(IConfiguration config, string? blogFilter = null) { string? root = config["appSettings:PathInput"]; if (string.IsNullOrWhiteSpace(root) || !Directory.Exists(root)) { Console.WriteLine($"PathInput is not set or does not exist: '{root}'. Nothing to revert."); return 0; } Console.WriteLine($"Searching for .bak files under: {root}"); // Recursively collect every *.bak, optionally filtered by path substring (blogname). var bakFiles = EnumerateBakFiles(root) .Where(f => string.IsNullOrWhiteSpace(blogFilter) || f.IndexOf(blogFilter, StringComparison.OrdinalIgnoreCase) >= 0) .ToList(); if (bakFiles.Count == 0) { Console.WriteLine("No .bak files found. Nothing to revert."); return 0; } Console.Write($"WARNING: This will restore {bakFiles.Count} .bak file(s) over their .txt files. " + $"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; } int restored = 0, backedUp = 0; 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 {bakFile} -> {Path.GetFileName(txtPath)}"); } catch (Exception ex) { Console.WriteLine($" Error reverting {bakFile}: {ex.Message}"); } } Console.WriteLine($"\nRevert mode complete. Restored {restored} file(s); backed up {backedUp} current .txt file(s)."); return 0; } // Recursively yields every *.bak path under root. Per-directory try/catch so an // inaccessible folder doesn't abort the whole walk (mirrors TraverseDirectory). private static IEnumerable EnumerateBakFiles(string path) { string[] subDirs; try { subDirs = Directory.GetDirectories(path); } catch (Exception ex) { Console.WriteLine($" Skipping '{path}': {ex.Message}"); yield break; } foreach (var dir in subDirs) foreach (var bak in EnumerateBakFiles(dir)) yield return bak; string[] bakFiles; try { bakFiles = Directory.GetFiles(path, "*.bak"); } catch (Exception ex) { Console.WriteLine($" Skipping files in '{path}': {ex.Message}"); yield break; } foreach (var bak in bakFiles) yield return bak; } // 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; } } } }