using Microsoft.Extensions.Configuration;

namespace URLNotesGrabberCORE
{
    // Port of ThreeTxtFileHelper RunCorrectionMode (dry-run) and RunFullCorrectionMode (apply).
    // Scans a BAK directory of .txt files, parses posts with multi-line field support,
    // and either reports or applies content-column corrections to TL.db.Posts.
    // Apply path only writes non-empty values (mirrors original ThreeTxtFileHelper semantics)
    // and never touches engagement columns.
    public static class CorrectMode
    {
        public static int Run(IConfiguration config, string[] args, bool applyChanges)
        {
            DataAccess.EnsureTTFileHelperColumnsExist();

            // Resolve BAK path: explicit arg > appSettings:PathTTBackup > derive from PathTTRoot/PathInput
            string? bakRootPath = args.Length > 0 ? args[0] : config["appSettings:PathTTBackup"];
            if (string.IsNullOrWhiteSpace(bakRootPath))
            {
                string? root = config["appSettings:PathTTRoot"];
                if (string.IsNullOrWhiteSpace(root)) root = config["appSettings:PathInput"];
                if (!string.IsNullOrWhiteSpace(root))
                    bakRootPath = root.TrimEnd('\\', '/') + "_BAK\\";
            }

            if (string.IsNullOrWhiteSpace(bakRootPath) || !Directory.Exists(bakRootPath))
            {
                Console.WriteLine($"BAK directory not found: {bakRootPath}");
                return 1;
            }

            Console.WriteLine($"BAK source path: {bakRootPath}");
            Console.WriteLine(applyChanges
                ? "Correction mode: APPLY - non-empty fields from BAK overwrite DB columns\n"
                : "Correction mode: Dry-run - reports multi-line field updates available\n");

            string prefixesPath = config["appSettings:PathPrefixes"] ?? "prefixes.txt";
            if (!Path.IsPathRooted(prefixesPath))
                prefixesPath = Path.Combine(AppContext.BaseDirectory, prefixesPath);
            if (!File.Exists(prefixesPath))
            {
                Console.WriteLine($"Prefixes file not found: {prefixesPath}");
                return 1;
            }
            var allowedPrefixes = new HashSet<string>(File.ReadLines(prefixesPath), StringComparer.OrdinalIgnoreCase);

            if (applyChanges)
            {
                Console.Write("WARNING: This will overwrite non-empty fields in matching posts from BAK files. Continue? (yes/no): ");
                string? response = Console.ReadLine();
                if (string.IsNullOrWhiteSpace(response) || !response.Equals("yes", StringComparison.OrdinalIgnoreCase))
                {
                    Console.WriteLine("Operation cancelled.");
                    return 0;
                }
            }

            var bakTxtFiles = new List<string>();
            try
            {
                foreach (var dir in Directory.GetDirectories(bakRootPath, "*", SearchOption.AllDirectories))
                    bakTxtFiles.AddRange(Directory.GetFiles(dir, "*.txt"));
                bakTxtFiles.AddRange(Directory.GetFiles(bakRootPath, "*.txt"));
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error scanning BAK directory: {ex.Message}");
                return 1;
            }
            Console.WriteLine($"Found {bakTxtFiles.Count} file(s) in BAK directory\n");

            int totalPostsFound = 0;
            int postsWithUpdates = 0;
            int postsUpdated = 0;
            int postsNotFound = 0;
            var correctionLog = new List<string>();
            var updateLog = new List<string>();

            foreach (string bakFile in bakTxtFiles)
            {
                Console.WriteLine($"Processing BAK file: {Path.GetFileName(bakFile)}");
                try
                {
                    var bakPosts = ParsePostsFromFile(bakFile, allowedPrefixes);
                    Console.WriteLine($"  Found {bakPosts.Count} post(s) in this file");

                    foreach (var (postId, bakData) in bakPosts)
                    {
                        totalPostsFound++;
                        var dbPost = DataAccess.GetPostByIdAnyBlog(postId);
                        if (dbPost == null)
                        {
                            postsNotFound++;
                            continue;
                        }

                        if (applyChanges)
                        {
                            bool updated = DataAccess.UpdatePostContentFields(dbPost.BlogName, dbPost.PostId, bakData);
                            if (updated)
                            {
                                postsUpdated++;
                                updateLog.Add($"Post ID: {postId} - Updated from {Path.GetFileName(bakFile)}");
                            }
                        }
                        else
                        {
                            var updateList = BuildDryRunDiff(bakData, dbPost);
                            if (updateList.Count > 0)
                            {
                                postsWithUpdates++;
                                correctionLog.Add($"\nPost ID: {postId}");
                                correctionLog.Add($"  File: {Path.GetFileName(bakFile)}");
                                correctionLog.Add($"  Fields to update:");
                                correctionLog.AddRange(updateList);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"  ERROR processing file: {ex.Message}");
                }
            }

            if (applyChanges)
            {
                Console.WriteLine($"\n========== CORRECTION COMPLETE ==========");
                Console.WriteLine($"Total posts found in BAK files: {totalPostsFound}");
                Console.WriteLine($"Posts updated in database:      {postsUpdated}");
                Console.WriteLine($"Posts not found in database:    {postsNotFound}");

                string logPath = config["appSettings:PathCorrectionApplied"] ?? "correction_applied.txt";
                try
                {
                    var logLines = new List<string>
                    {
                        $"Correction Applied: {DateTime.Now:yyyy-MM-dd HH:mm:ss}",
                        $"Total posts found in BAK files: {totalPostsFound}",
                        $"Posts updated in database:      {postsUpdated}",
                        $"Posts not found in database:    {postsNotFound}",
                        "",
                        "Updated Posts:"
                    };
                    logLines.AddRange(updateLog);
                    File.WriteAllLines(logPath, logLines);
                    Console.WriteLine($"Update log saved to: {logPath}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error writing log file: {ex.Message}");
                }
            }
            else
            {
                Console.WriteLine($"\n========== CORRECTION REPORT (DRY RUN) ==========");
                Console.WriteLine($"Total posts found in BAK files: {totalPostsFound}");
                Console.WriteLine($"Posts with multi-line field updates available: {postsWithUpdates}");

                if (correctionLog.Count > 0)
                {
                    string logPath = config["appSettings:PathCorrectionReport"] ?? "correction_report.txt";
                    try
                    {
                        File.WriteAllLines(logPath, correctionLog);
                        Console.WriteLine($"\nDetailed report saved to: {logPath}");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"Error writing report file: {ex.Message}");
                    }
                }
                else
                {
                    Console.WriteLine("\nNo multi-line field updates found.");
                }

                Console.WriteLine("\nDry-run complete. No database changes were made.");
                Console.WriteLine("If updates look correct, re-run with `-correct -apply` to apply changes.");
            }
            return 0;
        }

        private static List<string> BuildDryRunDiff(Dictionary<string, string> bakData, TTPostRecord dbPost)
        {
            var updates = new List<string>();
            foreach (var (fieldName, bakValue) in bakData)
            {
                if (string.IsNullOrWhiteSpace(bakValue)) continue;
                string? currentValue = fieldName.ToLowerInvariant() switch
                {
                    "reblog url" => dbPost.ReblogUrl,
                    "date" => dbPost.Date,
                    "has image" => dbPost.HasImage,
                    "post url" => dbPost.PostUrl,
                    "slug" => dbPost.Slug,
                    "reblog key" => dbPost.ReblogKey,
                    "reblog name" => dbPost.ReblogName,
                    "summary" => dbPost.Summary,
                    "quote" => dbPost.Quote,
                    "body" => dbPost.Body,
                    "tags" => dbPost.Tags,
                    "link" => dbPost.Link,
                    "photo url" => dbPost.PhotoUrl,
                    "photo caption" => dbPost.PhotoCaption,
                    "downloaded files" => dbPost.DownloadedFiles,
                    "audio caption" => dbPost.AudioCaption,
                    "question" => dbPost.Question,
                    "answer" => dbPost.Answer,
                    "title" => dbPost.Title,
                    _ => null
                };
                if (bakValue != currentValue && bakValue.Contains('\n'))
                    updates.Add($"    {fieldName}: [MULTILINE]");
            }
            return updates;
        }

        private static Dictionary<string, Dictionary<string, string>> ParsePostsFromFile(string filePath, HashSet<string> allowedPrefixes)
        {
            var posts = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
            var lines = File.ReadAllLines(filePath);
            int lineIndex = 0;
            string currentPostId = "";
            var currentPostData = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

            while (lineIndex < lines.Length)
            {
                string line = lines[lineIndex];
                string searchText = line.Length > 25 ? line.Substring(0, 25) : line;
                int colonIndex = searchText.IndexOf(": ");

                if (colonIndex > 0)
                {
                    string prefix = line.Substring(0, colonIndex).Trim();
                    if (!string.IsNullOrWhiteSpace(prefix) && allowedPrefixes.Contains(prefix))
                    {
                        if (string.Equals(prefix, "Post ID", StringComparison.OrdinalIgnoreCase))
                        {
                            if (!string.IsNullOrWhiteSpace(currentPostId) && currentPostData.Count > 0)
                                posts[currentPostId] = new Dictionary<string, string>(currentPostData, StringComparer.OrdinalIgnoreCase);
                            currentPostId = line.Substring(colonIndex + 2).Trim();
                            currentPostData.Clear();
                            lineIndex++;
                            continue;
                        }

                        var valueLines = new List<string> { line.Substring(colonIndex + 2).Trim() };
                        int nextLineIndex = lineIndex + 1;
                        while (nextLineIndex < lines.Length)
                        {
                            string nextLine = lines[nextLineIndex];
                            string nextSearch = nextLine.Length > 25 ? nextLine.Substring(0, 25) : nextLine;
                            int nextColon = nextSearch.IndexOf(": ");
                            if (nextColon > 0)
                            {
                                string nextPrefix = nextLine.Substring(0, nextColon).Trim();
                                if (!string.IsNullOrWhiteSpace(nextPrefix) && allowedPrefixes.Contains(nextPrefix))
                                    break;
                            }
                            valueLines.Add(nextLine);
                            nextLineIndex++;
                        }
                        currentPostData[prefix] = string.Join("\n", valueLines);
                        lineIndex = nextLineIndex;
                        continue;
                    }
                }
                lineIndex++;
            }
            if (!string.IsNullOrWhiteSpace(currentPostId) && currentPostData.Count > 0)
                posts[currentPostId] = new Dictionary<string, string>(currentPostData, StringComparer.OrdinalIgnoreCase);

            return posts;
        }
    }
}
