using System.Formats.Tar;
using System.IO.Compression;

using SharedTools;

namespace Build.Commands;

public static class Pdfium
{
    private static readonly HttpClient CLIENT = new()
    {
        Timeout = TimeSpan.FromMinutes(5)
    };

    public static async Task InstallAsync(RID rid, string version, bool offline)
    {
        Console.Write($"- Installing Pdfium {version} for {rid.ToUserFriendlyName()} ...");

        var cwd = Environment.GetRustRuntimeDirectory();
        var pdfiumUrl = GetPdfiumDownloadUrl(rid, version);
        var library = GetLibraryPath(rid);
        var pdfiumLibTargetPath = Path.Join(cwd, "resources", "libraries", library.Filename);

        if (offline)
        {
            if (File.Exists(pdfiumLibTargetPath))
            {
                Console.WriteLine(" offline mode enabled and library already exists, skipping download");
                return;
            }

            Console.WriteLine($" failed because offline mode is enabled and '{pdfiumLibTargetPath}' does not exist");
            return;
        }

        if (string.IsNullOrWhiteSpace(library.Path))
        {
            Console.WriteLine($" failed to find the library path for {rid.ToUserFriendlyName()}");
            return;
        }

        var pdfiumLibTargetDirectory = Path.Join(cwd, "resources", "libraries");
        var pdfiumLibTmpTargetPath = Path.Join(pdfiumLibTargetDirectory, $"{library.Filename}.{Guid.NewGuid():N}.tmp");
        var pdfiumLibArchivePath = library.Path.Replace('\\', '/');

        //
        // Download the file:
        //
        Console.Write(" downloading ...");
        using var response = await CLIENT.GetAsync(pdfiumUrl, HttpCompletionOption.ResponseHeadersRead);
        if (!response.IsSuccessStatusCode)
        {
            Console.WriteLine($" failed to download Pdfium {version} for {rid.ToUserFriendlyName()} from {pdfiumUrl}");
            return;
        }

        //
        // Extract the library from the downloaded file:
        //
        Console.Write(" extracting ...");
        Directory.CreateDirectory(pdfiumLibTargetDirectory);

        var foundLibrary = false;
        try
        {
            await using var downloadStream = await response.Content.ReadAsStreamAsync();
            await using var uncompressedStream = new GZipStream(downloadStream, CompressionMode.Decompress);
            await using var tarReader = new TarReader(uncompressedStream);

            while (await tarReader.GetNextEntryAsync() is { } entry)
            {
                if (!string.Equals(entry.Name.Replace('\\', '/'), pdfiumLibArchivePath, StringComparison.Ordinal))
                    continue;

                if (entry.DataStream == null)
                    break;

                await using var fileStream = File.Create(pdfiumLibTmpTargetPath);
                await entry.DataStream.CopyToAsync(fileStream);
                foundLibrary = true;
                break;
            }

            if (!foundLibrary)
            {
                Console.WriteLine($" failed to find the library file '{pdfiumLibArchivePath}' in the Pdfium archive");
                return;
            }

            Console.Write(" deploying ...");
            File.Move(pdfiumLibTmpTargetPath, pdfiumLibTargetPath, true);
        }
        finally
        {
            if (File.Exists(pdfiumLibTmpTargetPath))
                File.Delete(pdfiumLibTmpTargetPath);
        }

        Console.WriteLine(" done.");
    }
    
    private static Library GetLibraryPath(RID rid) => rid switch
    {
        RID.LINUX_ARM64 or RID.LINUX_X64 => new(Path.Join("lib", "libpdfium.so"), "libpdfium.so"),
        RID.OSX_ARM64 or RID.OSX_X64 => new(Path.Join("lib", "libpdfium.dylib"), "libpdfium.dylib"),
        RID.WIN_ARM64 or RID.WIN_X64 => new(Path.Join("bin", "pdfium.dll"), "pdfium.dll"),
        
        _ => new(string.Empty, string.Empty),
    };

    private static string GetPdfiumDownloadUrl(RID rid, string version)
    {
        var baseUrl = $"https://github.com/bblanchon/pdfium-binaries/releases/download/chromium%2F{version}/pdfium-";
        return rid switch
        {
            RID.LINUX_ARM64 => $"{baseUrl}linux-arm64.tgz",
            RID.LINUX_X64 => $"{baseUrl}linux-x64.tgz",
            
            RID.OSX_ARM64 => $"{baseUrl}mac-arm64.tgz",
            RID.OSX_X64 => $"{baseUrl}mac-x64.tgz",
            
            RID.WIN_ARM64 => $"{baseUrl}win-arm64.tgz",
            RID.WIN_X64 => $"{baseUrl}win-x64.tgz",
            
            _ => string.Empty,
        };
    }
}