using File_Folder_Helper.Helpers;
using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace File_Folder_Helper.ADO2024.PI2;

internal static partial class Helper20240623
{

    [GeneratedRegex("([A-Z]+(.))")]
    private static partial Regex UpperCase();

    [GeneratedRegex("[\\s!?.,@:;|\\\\/\"'`£$%\\^&*{}[\\]()<>~#+\\-=_¬]+")]
    private static partial Regex InvalidCharacter();

    private record H1AndParamCase(string H1, string ParamCase);
    private record SubTaskLine(string Text, bool Started, bool Completed, long? Ticks, int? Line);
    private record Record(int? CodeInsidersLine, FileInfo FileInfo, LineNumber LineNumber, int? StopLine, int? SubTasksLine);

    private record Input(long? AfterEpochTotalMilliseconds,
                         string CodeInsiders,
                         ReadOnlyCollection<string> DestinationDirectories,
                         string DirectoryFilter,
                         string Done,
                         string IndexFile,
                         string SearchPattern,
                         string SubTasks,
                         string SourceDirectory,
                         ReadOnlyCollection<string> Tasks);

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Input))]
    private partial class InputSourceGenerationContext : JsonSerializerContext
    {
    }

    private static Record GetRecord(Input input, FileInfo fileInfo)
    {
        Record result;
        string line;
        int? stopLine = null;
        int? subTasksLine = null;
        int? codeInsidersLine = null;
        LineNumber lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
        for (int i = 0; i < lineNumber.Lines.Count; i++)
        {
            line = lineNumber.Lines[i];
            if (line.StartsWith(input.CodeInsiders) && line[^1] == ')')
                codeInsidersLine = i;
            if (line != input.SubTasks)
                continue;
            subTasksLine = i;
            if (codeInsidersLine is null)
                break;
            if (lineNumber.Lines.Count > i)
            {
                for (int j = i + 1; j < lineNumber.Lines.Count; j++)
                {
                    if (lineNumber.Lines[j].Length > 0 && lineNumber.Lines[j][0] == '#')
                    {
                        stopLine = j;
                        break;
                    }
                }
            }
            stopLine ??= lineNumber.Lines.Count;
            break;
        }
        result = new(codeInsidersLine, fileInfo, lineNumber, stopLine, subTasksLine);
        return result;
    }

    private static List<Record> GetRecords(Input input)
    {
        List<Record> results = [];
        Record record;
        FileInfo fileInfo;
        string sourceDirectory = input.SourceDirectory;
        ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(input.SourceDirectory);
        if (!directoryNames.Any(l => l.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase)))
        {
            string directoryName;
            string[] checkDirectories = Directory.GetDirectories(input.SourceDirectory, "*", SearchOption.TopDirectoryOnly);
            foreach (string checkDirectory in checkDirectories)
            {
                directoryName = Path.GetFileName(checkDirectory);
                if (directoryName.StartsWith(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
                {
                    sourceDirectory = checkDirectory;
                    break;
                }
            }
        }
        string[] subDirectories = Directory.GetDirectories(sourceDirectory, "*", SearchOption.TopDirectoryOnly);
        List<string> files = Directory.GetFiles(sourceDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly).ToList();
        foreach (string subDirectory in subDirectories)
            files.AddRange(Directory.GetFiles(subDirectory, input.SearchPattern, SearchOption.TopDirectoryOnly));
        foreach (string file in files)
        {
            fileInfo = new(file);
            record = GetRecord(input, fileInfo);
            results.Add(record);
        }
        return results;
    }

    private static string GetParamCase(string value)
    {
        string result;
        StringBuilder stringBuilder = new(value);
        Match[] matches = UpperCase().Matches(value).ToArray();
        for (int i = matches.Length - 1; i > -1; i--)
            _ = stringBuilder.Insert(matches[i].Index, '-');
        string[] segments = InvalidCharacter().Split(stringBuilder.ToString().ToLower());
        result = string.Join('-', segments).Trim('-');
        return result;
    }

    private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, bool? foundStarted, bool? foundCompleted, string fallbackLine, Record record)
    {
        List<SubTaskLine> results = [];
        char done;
        string line;
        string text;
        bool startedValue;
        bool completedValue;
        SubTaskLine subTaskLine;
        bool foundSubTasks = false;
        int tasksZeroLength = input.Tasks[0].Length;
        long ticks = record.FileInfo.LastWriteTime.Ticks;
        for (int i = 0; i < record.LineNumber.Lines.Count; i++)
        {
            line = record.LineNumber.Lines[i];
            if (!foundSubTasks && line == input.SubTasks)
                foundSubTasks = true;
            if (!foundSubTasks)
                continue;
            if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
                continue;
            startedValue = foundStarted is not null && foundStarted.Value;
            completedValue = foundCompleted is not null && foundCompleted.Value;
            subTaskLine = new(Text: $"  {line}", Started: startedValue, Completed: completedValue, Ticks: ticks, Line: i);
            results.Add(subTaskLine);
        }
        startedValue = foundStarted is not null && foundStarted.Value;
        completedValue = foundCompleted is not null && foundCompleted.Value;
        if (record.LineNumber.H1 is null)
            subTaskLine = new(Text: fallbackLine, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: null);
        else
        {
            done = foundCompleted is null || !foundCompleted.Value ? ' ' : 'x';
            string codeInsidersLine = record.CodeInsidersLine is null ? string.Empty : $" ~~{record.LineNumber.Lines[record.CodeInsidersLine.Value]}~~";
            text = $"- [{done}] {ticks} {record.LineNumber.Lines[record.LineNumber.H1.Value]}{codeInsidersLine}";
            subTaskLine = new(Text: text, Started: startedValue, Completed: completedValue, Ticks: ticks, Line: 0);
        }
        results.Add(subTaskLine);
        return new(results);
    }

    private static string GetSeasonName(int dayOfYear)
    {
        string result = dayOfYear switch
        {
            < 78 => "0.Winter",
            < 124 => "1.Spring",
            < 171 => "2.Spring",
            < 217 => "3.Summer",
            < 264 => "4.Summer",
            < 309 => "5.Fall",
            < 354 => "6.Fall",
            _ => "7.Winter"
        };
        return result;
    }

    private static string[] GetIndexLines(string h1, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection) =>
        [
            "---",
            "startedColumns:",
            "  - 'In Progress'",
            "completedColumns:",
            "  - Done",
            "---",
            string.Empty,
            h1[0] == '#' ? h1 : $"# {h1}",
            string.Empty,
            "## Backlog",
            string.Empty,
            string.Join(Environment.NewLine, h1ParamCaseCollection.Select(l => $"- [{l.ParamCase}](tasks/{l.ParamCase}.md)")),
            string.Empty,
            "## Todo",
            string.Empty,
            "## In Progress",
            string.Empty,
            "## Done",
            string.Empty
        ];

    private static string[] GetCascadingStyleSheetsLines() =>
    [
        ".kanbn-column-done .kanbn-column-task-list {",
        "  border-color: #198038;",
        "}",
        string.Empty,
        ".kanbn-task-data-created {",
        "  display: none;",
        "}",
        string.Empty,
        ".kanbn-task-data-workload {",
        "  display: none;",
        "}"
    ];

    private static string GetSettingsLines() =>
    /*lang=json,strict*/ """
    {
        "[markdown]": {
            "editor.wordWrap": "off"
        },
        "cSpell.words": [
            "kanbn"
        ]
    }
    """;

    private static string GetTasksLines(string directory) =>
    /*lang=json,strict*/ """
    {
        "version": "2.0.0",
        "tasks": [
            {
                "label": "File-Folder-Helper AOT s X Day-Helper-2024-06-23",
                "type": "shell",
                "command": "L:/DevOps/Mesa_FI/File-Folder-Helper/bin/Release/net8.0/win-x64/publish/File-Folder-Helper.exe",
                "args": [
                    "s",
                    "X",
                    "{}",
                    "Day-Helper-2024-06-23",
                    "*.md",
                    "##_Sub-tasks",
                    "-_[code-insiders](",
                    "index.md",
                    "-_[,](",
                    "##_Done",
                    ".kan",
                    "D:/5-Other-Small/Kanban/Year-Season",
                    "316940400000"
                ],
                "problemMatcher": []
            }
        ]
    }
    """.Replace("{}", directory.Replace('\\', '/'));

    private static void FileWriteAllText(string path, string contents)
    {
        // string checkJson = Regex.Replace(File.ReadAllText(path), @"\s+", " ", RegexOptions.Multiline);
        // if (Regex.Replace(singletonJson, @"\s+", " ", RegexOptions.Multiline) != checkJson)
        //     File.WriteAllText(path, singletonJson);
        string old = !File.Exists(path) ? string.Empty : File.ReadAllText(path);
        if (old != contents)
            File.WriteAllText(path, contents);
    }

    private static void FileWriteAllText(string path, string[] contents) =>
        FileWriteAllText(path, string.Join(Environment.NewLine, contents));

    private static ReadOnlyCollection<H1AndParamCase> GetH1ParamCaseCollection(Input input, ReadOnlyCollection<string> lines)
    {
        List<H1AndParamCase> results = [];
        string h1;
        string line;
        string paramCase;
        bool foundSubTasks = false;
        H1AndParamCase h1AndParamCase;
        int tasksZeroLength = input.Tasks[0].Length;
        for (int i = 0; i < lines.Count; i++)
        {
            line = lines[i];
            if (!foundSubTasks && line == input.SubTasks)
                foundSubTasks = true;
            if (!foundSubTasks)
                continue;
            if (line.Length <= tasksZeroLength || !line.StartsWith(input.Tasks[0]) || line[tasksZeroLength] is not ' ' and not 'x' || line[tasksZeroLength + 1] != ']')
                continue;
            h1 = line[(tasksZeroLength + 3)..];
            if (string.IsNullOrEmpty(h1))
                continue;
            paramCase = GetParamCase(h1);
            h1AndParamCase = new(h1, paramCase);
            results.Add(h1AndParamCase);
        }
        return results.AsReadOnly();
    }

    private static void CreateFiles(string directory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
    {
        foreach (H1AndParamCase h1ParamCase in h1ParamCaseCollection)
            FileWriteAllText(Path.Combine(directory, $"{h1ParamCase.ParamCase}.md"), $"# {h1ParamCase.H1}");
    }

    private static string WriteAndGetIndexFile(string h1, string verifiedDirectory, ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection)
    {
        string result;
        string[] indexLines = GetIndexLines(h1, h1ParamCaseCollection);
        string kanbanDirectory = Path.Combine(verifiedDirectory, ".kanbn");
        string tasksKanbanDirectory = Path.Combine(kanbanDirectory, "tasks");
        if (!Directory.Exists(tasksKanbanDirectory))
            _ = Directory.CreateDirectory(tasksKanbanDirectory);
        string verifiedVisualStudioCodeDirectory = Path.Combine(verifiedDirectory, ".vscode");
        if (!Directory.Exists(verifiedVisualStudioCodeDirectory))
            _ = Directory.CreateDirectory(verifiedVisualStudioCodeDirectory);
        result = Path.Combine(kanbanDirectory, "index.md");
        CreateFiles(tasksKanbanDirectory, h1ParamCaseCollection);
        FileWriteAllText(result, indexLines);
        FileWriteAllText(Path.Combine(kanbanDirectory, "board.css"), GetCascadingStyleSheetsLines());
        FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "settings.json"), GetSettingsLines());
        FileWriteAllText(Path.Combine(verifiedVisualStudioCodeDirectory, "tasks.json"), GetTasksLines(verifiedDirectory));
        return result;
    }

    private static ReadOnlyCollection<string> GetXColumns(Input input, int frontMatterYamlEnd, int value, ReadOnlyCollection<string> lines)
    {
        List<string> results = [];
        string[] segments;
        for (int i = value + 1; i < frontMatterYamlEnd; i++)
        {
            segments = lines[i].Replace("\t", "  ").Split("  - ");
            if (segments.Length != 2)
                break;
            results.Add($"## {segments[1].Replace("'", string.Empty)}");
        }
        if (results.Count == 0)
            results.Add(input.Done);
        return results.AsReadOnly();
    }

    private static ReadOnlyCollection<string> GetCompletedColumns(Input input, LineNumber lineNumber)
    {
        List<string> results;
        if (lineNumber.FrontMatterYamlEnd is null || lineNumber.CompletedColumns is null)
            results = [];
        else
            results = GetXColumns(input, lineNumber.FrontMatterYamlEnd.Value, lineNumber.CompletedColumns.Value, lineNumber.Lines).ToList();
        if (results.Count == 0)
            results.Add(input.Done);
        return results.AsReadOnly();
    }

    private static ReadOnlyCollection<string> GetStartedColumns(Input input, LineNumber lineNumber)
    {
        List<string> results;
        if (lineNumber.FrontMatterYamlEnd is null || lineNumber.StartedColumns is null)
            results = [];
        else
            results = GetXColumns(input, lineNumber.FrontMatterYamlEnd.Value, lineNumber.StartedColumns.Value, lineNumber.Lines).ToList();
        if (results.Count == 0)
            results.Add(input.Done);
        return results.AsReadOnly();
    }

    private static ReadOnlyCollection<SubTaskLine> GetSubTaskLines(Input input, FileInfo fileInfo, LineNumber lineNumber)
    {
        List<SubTaskLine> results = [];
        FileInfo f;
        Record record;
        char completed;
        string[] segments;
        bool startedValue;
        bool completedValue;
        string fallbackLine;
        bool? foundStarted = null;
        bool? foundCompleted = null;
        ReadOnlyCollection<SubTaskLine> subTaskLines;
        ReadOnlyCollection<string> startedColumns = GetStartedColumns(input, lineNumber);
        ReadOnlyCollection<string> completedColumns = GetCompletedColumns(input, lineNumber);
        int start = lineNumber.FrontMatterYamlEnd is null ? 0 : lineNumber.FrontMatterYamlEnd.Value + 1;
        for (int i = start; i < lineNumber.Lines.Count; i++)
        {
            if ((foundStarted is null || !foundStarted.Value) && startedColumns.Any(lineNumber.Lines[i].StartsWith))
                foundStarted = true;
            if ((foundCompleted is null || !foundCompleted.Value) && completedColumns.Any(lineNumber.Lines[i].StartsWith))
                foundCompleted = true;
            segments = lineNumber.Lines[i].Split(input.Tasks[1]);
            startedValue = foundStarted is not null && foundStarted.Value;
            completedValue = foundCompleted is not null && foundCompleted.Value;
            if (segments.Length > 2 || !segments[0].StartsWith(input.Tasks[0]))
                continue;
            completed = foundCompleted is null || !foundCompleted.Value ? ' ' : 'x';
            fallbackLine = $"- [{completed}] {segments[0][input.Tasks[0].Length..]} ~~FallbackLine~~";
            if (string.IsNullOrEmpty(fileInfo.DirectoryName))
                continue;
            f = new(Path.GetFullPath(Path.Combine(fileInfo.DirectoryName, segments[1][..^1])));
            if (!f.Exists)
            {
                results.Add(new(Text: fallbackLine, Started: startedValue, Completed: completedValue, Ticks: null, Line: null));
                continue;
            }
            record = GetRecord(input, f);
            subTaskLines = GetSubTaskLines(input, startedValue, completedValue, fallbackLine, record);
            for (int j = subTaskLines.Count - 1; j >= 0; j--)
                results.Add(subTaskLines[j]);
        }
        return results.AsReadOnly();
    }

    private static Input GetInput(List<string> args)
    {
        string indexFile = args[5];
        string searchPattern = args[2];
        string directoryFilter = args[8];
        string done = args[7].Replace('_', ' ');
        string subTasks = args[3].Replace('_', ' ');
        string codeInsiders = args[4].Replace('_', ' ');
        string sourceDirectory = Path.GetFullPath(args[0]);
        string[] tasks = args[6].Split(',').Select(l => l.Replace('_', ' ')).ToArray();
        long? afterEpochTotalMilliseconds = args.Count < 11 ? null : long.Parse(args[10]);
        string[] destinationDirectories = args.Count < 10 ? [] : args.Count < 12 ? [Path.GetFullPath(args[9])] : [Path.GetFullPath(args[9]), Path.GetFullPath(args[11])];
        Input input = new(AfterEpochTotalMilliseconds: afterEpochTotalMilliseconds,
                          CodeInsiders: codeInsiders,
                          DestinationDirectories: destinationDirectories.AsReadOnly(),
                          DirectoryFilter: directoryFilter,
                          Done: done,
                          IndexFile: indexFile,
                          SearchPattern: searchPattern,
                          SubTasks: subTasks,
                          SourceDirectory: sourceDirectory,
                          Tasks: tasks.AsReadOnly());
        if (input.Tasks[0] != "- [" || input.Tasks[1] != "](")
            throw new Exception(JsonSerializer.Serialize(input, InputSourceGenerationContext.Default.Input));
        return input;
    }

    private static string? MaybeWriteAndGetIndexFile(Input input, Record record, string? checkDirectory)
    {
        string? result;
        if (string.IsNullOrEmpty(checkDirectory) || input.AfterEpochTotalMilliseconds is null || input.DestinationDirectories.Count == 0)
            result = null;
        else
        {
            if (!input.DestinationDirectories.Any(checkDirectory.Contains))
                result = null;
            else
            {
                if (record.LineNumber.H1 is null)
                    result = null;
                else
                {
                    string segment = Path.GetFileName(checkDirectory);
                    string h1 = record.LineNumber.Lines[record.LineNumber.H1.Value];
                    DateTime utcEpochDateTime = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
                    long utcEpochTotalMilliseconds = (long)Math.Floor(DateTime.UtcNow.Subtract(utcEpochDateTime).TotalMilliseconds);
                    if (!long.TryParse(segment, out long check) || check < input.AfterEpochTotalMilliseconds || check > utcEpochTotalMilliseconds)
                        result = null;
                    else
                    {
                        ReadOnlyCollection<H1AndParamCase> h1ParamCaseCollection = GetH1ParamCaseCollection(input, record.LineNumber.Lines);
                        if (h1ParamCaseCollection.Count == 0)
                            result = null;
                        else
                        {
                            DateTime dateTime = utcEpochDateTime.AddMilliseconds(check).ToLocalTime();
                            string seasonName = GetSeasonName(dateTime.DayOfYear);
                            ReadOnlyCollection<string> directoryNames = HelperDirectory.GetDirectoryNames(checkDirectory);
                            if (!directoryNames.Contains(dateTime.Year.ToString()) || !directoryNames.Contains($"{dateTime.Year}-{seasonName}") || !directoryNames.Contains(check.ToString()))
                                result = null;
                            else
                                result = WriteAndGetIndexFile(h1, checkDirectory, h1ParamCaseCollection);
                        }
                    }
                }
            }
        }
        return result;
    }

    private static bool FileWrite(long ticks, Record record, List<string> newLines, double percent)
    {
        bool result = false;
        if (record.StopLine is not null && record.SubTasksLine is not null)
        {
            string contents;
            string progressLine;
            List<string> resultLines;
            if (record.FileInfo.LastWriteTime.Ticks <= ticks)
                resultLines = record.LineNumber.Lines.ToList();
            else
                resultLines = File.ReadAllLines(record.FileInfo.FullName).ToList();
            if (record.LineNumber.FrontMatterYamlEnd is not null)
            {
                progressLine = $"progress: {percent}";
                if (record.LineNumber.Progress is not null)
                    resultLines[record.LineNumber.Progress.Value] = progressLine;
                else
                {
                    resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, progressLine);
                    contents = string.Join(Environment.NewLine, resultLines);
                    FileWriteAllText(record.FileInfo.FullName, contents);
                    result = true;
                }
                if (!result && record.LineNumber.Completed is null && percent > 99.9)
                {
                    resultLines.Insert(record.LineNumber.FrontMatterYamlEnd.Value, $"completed: {DateTime.Now:yyyy-MM-dd}");
                    contents = string.Join(Environment.NewLine, resultLines);
                    FileWriteAllText(record.FileInfo.FullName, contents);
                    result = true;
                }
                if (!result && record.LineNumber.Completed is not null && percent < 99.9)
                {
                    resultLines.RemoveAt(record.LineNumber.Completed.Value);
                    contents = string.Join(Environment.NewLine, resultLines);
                    FileWriteAllText(record.FileInfo.FullName, contents);
                    result = true;
                }
            }
            if (!result)
            {
                for (int i = record.StopLine.Value - 1; i > record.SubTasksLine.Value + 1; i--)
                    resultLines.RemoveAt(i);
                if (record.StopLine.Value == record.LineNumber.Lines.Count && resultLines[^1].Length == 0)
                    resultLines.RemoveAt(resultLines.Count - 1);
                for (int i = 0; i < newLines.Count; i++)
                    resultLines.Insert(record.SubTasksLine.Value + 1 + i, newLines[i]);
                resultLines.Insert(record.SubTasksLine.Value + 1, string.Empty);
                contents = string.Join(Environment.NewLine, resultLines);
                FileWriteAllText(record.FileInfo.FullName, contents);
            }
        }
        return result;
    }

    private static string? GetInferredCheckDirectory(string directory)
    {
        string? result = null;
        List<string> directoryNames = [];
        DirectoryInfo directoryInfo;
        string? checkDirectory = directory;
        directoryNames.Add(Path.GetFileName(checkDirectory));
        string pathRoot = Path.GetPathRoot(directory) ?? throw new Exception();
        for (int i = 0; i < directory.Length; i++)
        {
            checkDirectory = Path.GetDirectoryName(checkDirectory);
            if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
                break;
            directoryInfo = new(checkDirectory);
            if (!directoryInfo.Exists)
                directoryNames.Add(directoryInfo.Name);
            else
            {
                directoryNames.Reverse();
                result = string.IsNullOrEmpty(directoryInfo.LinkTarget) ? checkDirectory : directoryInfo.LinkTarget;
                for (int j = 0; j < directoryNames.Count; j++)
                    result = Path.GetDirectoryName(result) ?? throw new Exception();
                foreach (string directoryName in directoryNames)
                    result = Path.Combine(result, directoryName);
                break;
            }
        }
        return result;
    }

    private static void UpdateFileAndStartNewProcess(ILogger<Worker> logger, Input input, Record record, string inferredCheckDirectory)
    {
        if (record.CodeInsidersLine is null)
            throw new Exception();
        List<string> lines = record.LineNumber.Lines.ToList();
        lines[record.CodeInsidersLine.Value] = $"{input.CodeInsiders}{inferredCheckDirectory})";
        string text = string.Join(Environment.NewLine, lines);
        File.WriteAllText(record.FileInfo.FullName, text);
        record.FileInfo.Refresh();
        string file = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "Microsoft VS Code Insiders", "Code - Insiders.exe");
        try
        { _ = Process.Start(file, $"\"{inferredCheckDirectory}\""); }
        catch (Exception) { logger.LogWarning("Failed to start code-insiders!"); }
    }

    private static FileInfo GetIndexFileInfo(ILogger<Worker> logger, Input input, Record record)
    {
        FileInfo result;
        string? indexFile;
        List<string> results;
        if (record.CodeInsidersLine is null)
            throw new Exception();
        string codeInsidersLine = record.LineNumber.Lines[record.CodeInsidersLine.Value];
        string raw = codeInsidersLine[input.CodeInsiders.Length..^1];
        string checkDirectory = $"{raw[..2].ToUpper()}{raw[2..]}";
        if (!Directory.Exists(checkDirectory))
        {
            if (input.DestinationDirectories.Count > 0 && input.DestinationDirectories.Any(checkDirectory.Contains))
            {
                string? inferredCheckDirectory = GetInferredCheckDirectory(checkDirectory);
                if (!string.IsNullOrEmpty(inferredCheckDirectory))
                {
                    checkDirectory = inferredCheckDirectory;
                    _ = Directory.CreateDirectory(inferredCheckDirectory);
                    UpdateFileAndStartNewProcess(logger, input, record, inferredCheckDirectory);
                }
            }
        }
        if (!Directory.Exists(checkDirectory))
            results = [];
        else
        {
            results = Directory.GetFiles(checkDirectory, input.IndexFile, SearchOption.AllDirectories).ToList();
            if (results.Count != 1)
            {
                for (int i = results.Count - 1; i > -1; i--)
                {
                    if (!results[i].Contains(input.DirectoryFilter, StringComparison.CurrentCultureIgnoreCase))
                        results.RemoveAt(i);
                }
            }
            if (results.Count == 0)
            {
                indexFile = MaybeWriteAndGetIndexFile(input, record, checkDirectory);
                if (!string.IsNullOrEmpty(indexFile))
                    results.Add(indexFile);
                else
                    logger.LogInformation("<{checkDirectory}>", checkDirectory);
            }
        }
        result = results.Count == 0 ? new(Path.Combine(checkDirectory, input.IndexFile)) : new(results[0]);
        return result;
    }

    internal static void UpdateSubTasksInMarkdownFiles(ILogger<Worker> logger, List<string> args)
    {
        bool reload;
        int allCount;
        int lineCheck;
        double percent;
        string replace;
        FileInfo fileInfo;
        double startedCount;
        List<Record> records;
        double completedCount;
        LineNumber lineNumber;
        List<string> newLines;
        bool reloadAny = false;
        string? checkDirectory;
        List<string> oldLines = [];
        Input input = GetInput(args);
        string fileNameWithoutExtension;
        long ticks = DateTime.Now.Ticks;
        ReadOnlyCollection<SubTaskLine> subTaskLines;
        for (int z = 0; z < 9; z++)
        {
            records = GetRecords(input);
            foreach (Record record in from l in records orderby l.SubTasksLine is null, l.CodeInsidersLine is null select l)
            {
                if (record.SubTasksLine is null)
                    continue;
                fileNameWithoutExtension = Path.GetFileNameWithoutExtension(record.FileInfo.FullName);
                if (record.CodeInsidersLine is not null)
                    logger.LogInformation("<{file}> has [{subTasks}]", fileNameWithoutExtension, input.SubTasks);
                else
                {
                    logger.LogWarning("<{file}> has [{subTasks}] but doesn't have [{codeInsiders}]!", fileNameWithoutExtension, input.SubTasks, input.CodeInsiders);
                    continue;
                }
                if (record.StopLine is null)
                    continue;
                fileInfo = GetIndexFileInfo(logger, input, record);
                if (!fileInfo.Exists)
                {
                    logger.LogError("<{checkDirectory}> doesn't have a [{indexFile}]", fileInfo.DirectoryName, input.IndexFile);
                    continue;
                }
                oldLines.Clear();
                checkDirectory = fileInfo.DirectoryName;
                lineNumber = HelperMarkdown.GetLineNumbers(fileInfo);
                subTaskLines = GetSubTaskLines(input, fileInfo, lineNumber);
                if (subTaskLines.Count == 0)
                    continue;
                lineCheck = 0;
                for (int i = record.SubTasksLine.Value + 1; i < record.StopLine.Value - 1; i++)
                    oldLines.Add(record.LineNumber.Lines[i]);
                if (subTaskLines.Count == 0)
                {
                    percent = 0;
                    replace = "0";
                }
                else
                {
                    allCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 select 1).Count();
                    completedCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Completed select 1).Count();
                    startedCount = (from l in subTaskLines where l.Line is not null && l.Line.Value == 0 && l.Started && !l.Completed select 1).Count();
                    percent = allCount == 0 ? 0 : Math.Round(completedCount / allCount, 3);
                    // newLines.Insert(0, $"- [{done}] Sub-tasks {doneCount} of {allCount} [{percent * 100}%]");
                    replace = $"{allCount} » {startedCount} ✓ {completedCount} {Math.Floor(percent * 100)}%".Replace(" ✓ 0 0%", string.Empty).Replace(" 100%", string.Empty).Replace(" » 0", string.Empty);
                }
                if (subTaskLines.Any(l => l.Ticks is null))
                    newLines = (from l in subTaskLines
                                select l.Text).ToList();
                else
                {
                    newLines = (from l in subTaskLines
                                orderby l.Completed descending, l.Started descending, l.Ticks, l.Line
                                select l.Text.Replace($"{l.Ticks}", replace)).ToList();
                }
                if (newLines.Count == oldLines.Count)
                {
                    for (int i = 0; i < newLines.Count; i++)
                    {
                        if (newLines[i] != record.LineNumber.Lines[record.SubTasksLine.Value + 1 + i])
                            continue;
                        lineCheck++;
                    }
                    if (lineCheck == newLines.Count)
                        continue;
                }
                if (string.IsNullOrEmpty(checkDirectory))
                    continue;
                checkDirectory = Path.Combine(checkDirectory, DateTime.Now.Ticks.ToString());
                _ = Directory.CreateDirectory(checkDirectory);
                Thread.Sleep(500);
                Directory.Delete(checkDirectory);
                reload = FileWrite(ticks, record, newLines, percent);
                if (!reloadAny && reload)
                    reloadAny = true;
            }
            if (!reloadAny)
                break;
        }
    }

}