using File_Folder_Helper.Models;
using Microsoft.Extensions.Logging;
#if ShellProgressBar
using ShellProgressBar;
#endif
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Text.Json;

namespace File_Folder_Helper.ADO2024.PI4;

internal static partial class Helper20241224
{

    private static readonly HttpClient _HttpClient = new();

    private record Record(Uri URI, string Path, DateTime LastModified);

    private static ReadOnlyCollection<NginxFileSystem>? GetRecursiveCollection(string host, string page)
    {
        List<NginxFileSystem>? results;
        Uri uri = new($"https://{host}/{page}");
        string format = NginxFileSystem.GetFormat();
        TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
        Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
        taskHttpResponseMessage.Wait();
        if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
            results = null;
        else
        {
            Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
            taskString.Wait();
            NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
            if (nginxFileSystems is null)
                results = null;
            else
            {
                results = [];
                NginxFileSystem nginxFileSystem;
                ReadOnlyCollection<NginxFileSystem>? directory;
                for (int i = 0; i < nginxFileSystems.Length; i++)
                {
                    nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
                    if (nginxFileSystem.Type == "file")
                        results.Add(nginxFileSystem);
                    else
                    {
                        directory = GetRecursiveCollection(host, $"{page}/{nginxFileSystem.Name}");
                        if (directory is null)
                            continue;
                        results.AddRange(directory);
                    }
                }
            }
        }
        return results?.AsReadOnly();
    }

    private static ReadOnlyCollection<NginxFileSystem>? GetCollection(string format, TimeZoneInfo timeZoneInfo, Uri uri)
    {
        List<NginxFileSystem>? results;
        Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(uri);
        taskHttpResponseMessage.Wait();
        if (!taskHttpResponseMessage.Result.IsSuccessStatusCode)
            results = null;
        else
        {
            Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
            taskString.Wait();
            if (taskString.Result.StartsWith('<'))
                results = null;
            else
            {
                NginxFileSystem[]? nginxFileSystems = JsonSerializer.Deserialize(taskString.Result, NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
                if (nginxFileSystems is null)
                    results = null;
                else
                {
                    results = [];
                    NginxFileSystem nginxFileSystem;
                    for (int i = 0; i < nginxFileSystems.Length; i++)
                    {
                        nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
                        results.Add(nginxFileSystem);
                    }
                }
            }
        }
        return results?.AsReadOnly();
    }

    private static Record? CompareFile(string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
    {
        Record? result;
        if (nginxFileSystem.LastModified is null || nginxFileSystem.Length is null)
            result = null;
        else
        {
            Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}/{nginxFileSystem.Name}");
            FileInfo fileInfo = new($"{compareDirectory}\\{string.Join('\\', directoryNames)}\\{nginxFileSystem.Name}");
            if (!fileInfo.Exists || fileInfo.Length != nginxFileSystem.Length.Value)
                result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
            else
            {
                double totalSeconds = new TimeSpan(fileInfo.LastWriteTime.Ticks - nginxFileSystem.LastModified.Value.Ticks).TotalSeconds;
                if (totalSeconds is < 2 and > -2)
                    result = null;
                else
                    result = new(uri, fileInfo.FullName, nginxFileSystem.LastModified.Value);
            }
        }
        return result;
    }

    private static ReadOnlyCollection<Record> CompareDirectory(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory, NginxFileSystem nginxFileSystem)
    {
        ReadOnlyCollection<Record> results;
        List<string> collection = directoryNames.ToList();
        collection.Add(nginxFileSystem.Name);
        results = GetRecord(format, timeZoneInfo, host, collection.AsReadOnly(), compareDirectory);
        return results;
    }

    private static ReadOnlyCollection<Record> GetRecord(string format, TimeZoneInfo timeZoneInfo, string host, ReadOnlyCollection<string> directoryNames, string compareDirectory)
    {
        List<Record> results = [];
        Uri uri = new($"https://{host}/{string.Join('/', directoryNames)}");
        ReadOnlyCollection<NginxFileSystem>? nginxFileSystems = GetCollection(format, timeZoneInfo, uri);
        if (nginxFileSystems is not null)
        {
            NginxFileSystem nginxFileSystem;
            ReadOnlyCollection<Record> records;
            string checkDirectory = $"{compareDirectory}\\{string.Join('\\', directoryNames)}";
            if (!Directory.Exists(checkDirectory))
                _ = Directory.CreateDirectory(checkDirectory);
            for (int i = 0; i < nginxFileSystems.Count; i++)
            {
                nginxFileSystem = NginxFileSystem.Get(format, timeZoneInfo, uri, nginxFileSystems[i]);
                if (nginxFileSystem.Type == "file")
                {
                    Record? record = CompareFile(host, directoryNames, compareDirectory, nginxFileSystem);
                    if (record is not null)
                        results.Add(record);
                }
                else
                {
                    records = CompareDirectory(format, timeZoneInfo, host, directoryNames, compareDirectory, nginxFileSystem);
                    foreach (Record record in records)
                        results.Add(record);
                }
            }
        }
        return results.AsReadOnly();
    }

    private static void Download(Record record)
    {
        Task<HttpResponseMessage> taskHttpResponseMessage = _HttpClient.GetAsync(record.URI);
        taskHttpResponseMessage.Wait();
        if (taskHttpResponseMessage.Result.IsSuccessStatusCode)
        {
            Task<string> taskString = taskHttpResponseMessage.Result.Content.ReadAsStringAsync();
            taskString.Wait();
            File.WriteAllText(record.Path, taskString.Result);
            File.SetLastWriteTime(record.Path, record.LastModified);
        }
    }

    internal static void Compare(ILogger<Worker> logger, List<string> args)
    {
        string host = args[2];
        string rootDirectoryName = args[3];
        string format = NginxFileSystem.GetFormat();
        TimeZoneInfo timeZoneInfo = TimeZoneInfo.Local;
        string compareDirectory = Path.GetFullPath(args[0]);
        logger.LogInformation("Comparing files on {host}", host);
        ReadOnlyCollection<Record> records = GetRecord(format, timeZoneInfo, host, new([rootDirectoryName]), compareDirectory);
#if ShellProgressBar
        ProgressBar progressBar = new(records.Count, "Downloading", new ProgressBarOptions() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true });
#endif
        foreach (Record record in records)
        {
#if ShellProgressBar
            progressBar.Tick();
#endif
            Download(record);
        }
#if ShellProgressBar
        progressBar.Dispose();
#endif
        if (Debugger.IsAttached)
        {
            ReadOnlyCollection<NginxFileSystem>? recursiveCollection = GetRecursiveCollection(host, rootDirectoryName);
            string? json = recursiveCollection is null ? null : JsonSerializer.Serialize(recursiveCollection.ToArray(), NginxFileSystemCollectionSourceGenerationContext.Default.NginxFileSystemArray);
            if (!string.IsNullOrEmpty(json))
                File.WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", ".json"), json);
        }
    }

}