using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace File_Folder_Helper.ADO2024.PI1;

internal static partial class Helper20240106
{

    private record Host([property: JsonPropertyName("a")] string? Id,
                       [property: JsonPropertyName("b")] string? Colon,
                       [property: JsonPropertyName("c")] string? Hyphen,
                       [property: JsonPropertyName("d")] string? Line,
                       [property: JsonPropertyName("e")] string? Count,
                       [property: JsonPropertyName("f")] string? Segments,
                       [property: JsonPropertyName("g")] string? Type,
                       [property: JsonPropertyName("h")] string? Device,
                       [property: JsonPropertyName("i")] string? Name,
                       [property: JsonPropertyName("j")] string? Location);

    [JsonSourceGenerationOptions(WriteIndented = true, AllowTrailingCommas = true)]
    [JsonSerializable(typeof(Host[]))]
    private partial class HostCollectionSourceGenerationContext : JsonSerializerContext
    {
    }

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Dictionary<string, Dictionary<string, string>>))]
    private partial class DictionaryDictionarySourceGenerationContext : JsonSerializerContext
    {
    }

    private record Record(string Key, Dictionary<string, string> KeyValuePairs);

    private static Dictionary<string, Dictionary<string, string>> GetKeyValuePairs(List<Record> collection, bool replaceFound)
    {
        Dictionary<string, Dictionary<string, string>> results = [];
        if (replaceFound)
        {
            foreach ((string key, Dictionary<string, string> keyValuePairs) in collection)
                _ = results.TryAdd(key, keyValuePairs);
        }
        else
        {
            foreach ((string key, Dictionary<string, string> keyValuePairs) in collection.OrderBy(l => l.Key))
                _ = results.TryAdd(key, keyValuePairs);
        }
        return results;
    }

    private static Dictionary<int, Host> GetHosts(string jsonl)
    {
        Dictionary<int, Host> results = [];
        int id;
        string json = $"[{File.ReadAllText(jsonl).Replace("\r\n", ",")}]";
        Host[] hosts = JsonSerializer.Deserialize(json, HostCollectionSourceGenerationContext.Default.HostArray) ?? throw new NullReferenceException(nameof(json));
        foreach (Host host in hosts)
        {
            if (host.Id is null)
                continue;
            if (host.Hyphen is not null and nameof(host.Hyphen))
                continue;
            if (!int.TryParse(host.Id, out id))
                throw new NotSupportedException($"{host.Id} is not a number");
            if (results.ContainsKey(id))
                throw new NotSupportedException($"Id {id} is not unique!");
            results.Add(id, host);
        }
        return results;
    }

    private static int? GetHeaderLine(string[] lines)
    {
        int? headerLine = null;
        for (int i = 0; i < lines.Length - 1; i++)
        {
            if (!lines[i].Contains('\t'))
                continue;
            headerLine = i;
        }
        return headerLine;
    }

    private static Dictionary<string, Dictionary<string, string>> GetKeyValuePairs(int keyIndex, int keyLength, string replace, string[] headers, string[] lines, int headerLine)
    {
        Dictionary<string, Dictionary<string, string>> results;
        string? key;
        Record record;
        bool replaceFound = false;
        List<Record> collection = [];
        Dictionary<string, string> keyValuePairs;
        for (int i = headerLine + 1; i < lines.Length; i++)
        {
            key = null;
            keyValuePairs = [];
            for (int j = 0; j < headers.Length; j++)
            {
                if (j > 0)
                    i++;
                if (lines.Length <= i)
                {
                    keyValuePairs.Clear();
                    break;
                }
                if (j == keyIndex)
                {
                    key = lines[i];
                    if (key.Length != keyLength)
                    {
                        keyValuePairs.Clear();
                        break;
                    }
                }
                if (lines[i] != replace)
                    _ = keyValuePairs.TryAdd(headers[j], lines[i]);
                else
                {
                    if (!replaceFound)
                        replaceFound = true;
                    _ = keyValuePairs.TryAdd(headers[j], lines[i]);
                    j++;
                    _ = keyValuePairs.TryAdd(headers[j], lines[i]);
                }
            }
            if (keyValuePairs.Count != headers.Length)
                continue;
            key ??= "-";
            record = new(key, keyValuePairs);
            collection.Add(record);
        }
        results = GetKeyValuePairs(collection, replaceFound);
        return results;
    }

    private static ReadOnlyCollection<string> GetIpAddressAndVerify(ILogger<Worker> logger, string key, Dictionary<string, Dictionary<string, string>> keyValuePairs, Dictionary<int, Host> hosts, string filter)
    {
        List<string> results = [];
        int id;
        bool found;
        Host? host;
        string text;
        string? ipAddress;
        StringBuilder stringBuilder = new();
        foreach (KeyValuePair<string, Dictionary<string, string>> keyValuePair in keyValuePairs)
        {
            found = false;
            if (keyValuePair.Key.StartsWith(filter))
                continue;
            if (!keyValuePair.Value.TryGetValue(key, out ipAddress))
                throw new NotSupportedException($"{key} isn't present!");
            if (ipAddress == "0.0.0.0")
                continue;
            results.Add(ipAddress);
            _ = stringBuilder.Clear();
            foreach (KeyValuePair<string, string> keyValue in keyValuePair.Value)
                _ = stringBuilder.AppendLine(keyValue.Value);
            text = stringBuilder.ToString();
            if (!int.TryParse(ipAddress.Split('.')[^1], out id))
                throw new NotSupportedException($"{ipAddress} isn't valid!");
            if (!hosts.TryGetValue(id, out host))
                throw new NotSupportedException($"{id} isn't valid!");
            foreach (KeyValuePair<string, string> keyValue in keyValuePair.Value)
            {
                if (keyValue.Value != host.Hyphen)
                    continue;
                found = true;
            }
            if (!found)
                throw new NotSupportedException($"{host}{Environment.NewLine}{text} doesn't match!");
            if (text.Contains("Unknown", StringComparison.InvariantCultureIgnoreCase))
                logger.LogWarning($"{text} contains Unknown and should be {host.Device}!");
        }
        return new(results);
    }

    private static void WriteAppendToHostConfFile(FileInfo fileInfo, string hostConfFile, Dictionary<int, Host> hosts, ReadOnlyCollection<string> ipAddress)
    {
        int id;
        Host host;
        string ip;
        string line;
        List<int> distinct = [];
        List<string> lines = [$"# {fileInfo.LastWriteTime.Ticks}"];
        string firstSegmentsOfIpAddress = string.Join('.', ipAddress[0].Split('.').Take(3));
        foreach (KeyValuePair<int, Host> keyValuePair in hosts)
        {
            host = keyValuePair.Value;
            if (host.Hyphen is not null and nameof(host.Hyphen))
                continue;
            if (host.Id is null || host.Hyphen is null || host.Device is null || host.Name is null || host.Hyphen.Length != 17)
                throw new NotSupportedException($"{host.Id} is Null or not 17");
            if (!int.TryParse(host.Id, out id))
                throw new NotSupportedException($"{host.Id} is not a number");
            if (distinct.Contains(id))
                throw new NotSupportedException($"{id} is not distinct!");
            distinct.Add(id);
            ip = ipAddress.Contains($"{firstSegmentsOfIpAddress}.{id}") ? $"{firstSegmentsOfIpAddress}.{id}" : $"# {firstSegmentsOfIpAddress}.{id}";
            line = $"{ip} {host.Name} # https://{host.Name} | {host.Colon} | {host.Hyphen} | {host.Device} |";
            lines.Add(line);
        }
        lines.Add($"# {fileInfo.LastWriteTime.Ticks}");
        File.AppendAllLines(hostConfFile, lines);
    }

    internal static void TextToJson(ILogger<Worker> logger, List<string> args)
    {
        string json;
        string[] lines;
        int? headerLine;
        FileInfo fileInfo;
        string key = args[7];
        string filter = args[10];
        string replace = args[5];
        int keyIndex = int.Parse(args[3]);
        int keyLength = int.Parse(args[4]);
        ReadOnlyCollection<string> ipAddress;
        string[] headers = args[6].Split(',');
        string jsonl = Path.Combine(args[0], args[8]);
        string hostConfFile = Path.Combine(args[0], args[9]);
        string[] txtFiles = Directory.GetFiles(args[0], args[2]);
        Dictionary<string, Dictionary<string, string>> keyValuePairs;
        if (!File.Exists(jsonl))
            throw new NotSupportedException($"{args[8]} doesn't exist!");
        Dictionary<int, Host> hosts = GetHosts(jsonl);
        if (hosts.Count == 0)
            throw new NotSupportedException($"{args[8]} isn't valid!");
        foreach (string txtFile in txtFiles)
        {
            lines = File.ReadAllLines(txtFile);
            if (lines.Length == 0)
                continue;
            headerLine = GetHeaderLine(lines);
            if (headerLine is null)
                continue;
            fileInfo = new(txtFile);
            keyValuePairs = GetKeyValuePairs(keyIndex, keyLength, replace, headers, lines, headerLine.Value);
            if (keyValuePairs.Count == 0)
                continue;
            ipAddress = GetIpAddressAndVerify(logger, key, keyValuePairs, hosts, filter);
            if (ipAddress.Count == 0)
                continue;
            json = JsonSerializer.Serialize(keyValuePairs, DictionaryDictionarySourceGenerationContext.Default.DictionaryStringDictionaryStringString);
            logger.LogInformation("Writing output file...");
            File.WriteAllText($"{fileInfo.FullName}-{fileInfo.LastWriteTime.Ticks}.json", json);
            WriteAppendToHostConfFile(fileInfo, hostConfFile, hosts, ipAddress);
            File.WriteAllLines(txtFile, [string.Empty, string.Empty, lines[^1]]);
        }
    }

}