using System.Net.Http.Json;
using System.Security.Cryptography;
using Lentia.Core.Constants;
using Lentia.Core.Utils;

namespace Lentia.Core.Game;

public class GenericArtifact {
    public string Path { get; set; } = string.Empty;
    public string Sha1 { get; set; } = string.Empty;
    public long Size { get; set; }
    public string Url { get; set; } = string.Empty;
}

public class GameFilesIndex {
    public List<ArtifactWrapper> Root { get; set; } = new();
}

public class DownloadsWrapper {
    public GenericArtifact Artifact { get; set; } = null!;
}

public class ArtifactWrapper {
    public DownloadsWrapper Downloads { get; set; } = null!;
    public string Name { get; set; } = string.Empty;
}

public static class GenericFilesService {

    public static async Task FetchAndSyncGenericFiles(string gameRoot) {
        try {
            var response = await HttpHelper.FetchAsync($"{LauncherConstants.Urls.ApiUrl}/api/v2/gamefiles", HttpMethod.Get);
            response.EnsureSuccessStatusCode();

            var index = await response.Content.ReadFromJsonAsync<GameFilesIndex>(LauncherConstants._jsonOptions);

            if (index?.Root != null) {
                var artifacts = index.Root.Select(r => r.Downloads.Artifact).ToList();
                await DownloadFiles(artifacts, gameRoot);
            }
        }
        catch (Exception ex) {
            Console.WriteLine($"[API Error] Impossible de récupérer les fichiers : {ex.Message}");
        }
    }
    
    public static async Task DownloadFiles(List<GenericArtifact> artifacts, string gameRoot) {
        var downloadQueue = new List<GenericArtifact>();

        foreach (var artifact in artifacts) {
            string localPath = Path.Combine(gameRoot, artifact.Path);
            
            if (!IsFileValid(localPath, artifact.Size, artifact.Sha1)) {
                downloadQueue.Add(artifact);
            }
        }

        if (downloadQueue.Count == 0) return;

        var options = new ParallelOptions { MaxDegreeOfParallelism = 4 };
        await Parallel.ForEachAsync(downloadQueue, options, async (artifact, token) => {
            string localPath = Path.Combine(gameRoot, artifact.Path);
            
            try {
                await FileHelper.DownloadFileAsync(artifact.Url, localPath);

                if (!IsFileValid(localPath, artifact.Size, artifact.Sha1)) {
                    throw new Exception($"Échec de la validation post-téléchargement pour : {artifact.Path}");
                }
            } catch (Exception ex) {
                Console.WriteLine($"[DownloadError] {artifact.Path} : {ex.Message}");
            }
        });
    }

    public static bool IsFileValid(string localPath, long expectedSize, string expectedSha1) {
        if (!File.Exists(localPath)) return false;

        var fileInfo = new FileInfo(localPath);
        if (fileInfo.Length != expectedSize) return false;

        string localHash = GetFileSha1(localPath);
        return localHash.Equals(expectedSha1, StringComparison.OrdinalIgnoreCase);
    }

    private static string GetFileSha1(string filePath) {
        using var stream = File.OpenRead(filePath);
        using var sha1 = SHA1.Create();
        byte[] hashBytes = sha1.ComputeHash(stream);
        return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
    }
}