using System.IO;
using System.IO.Compression;
using System.Text.Json;
using NexusShell.Models;

namespace NexusShell.Services;

public sealed class NexusPackageReader
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true,
    };

    public NexusPackageManifest? ReadManifestFromPackage(string packagePath)
    {
        if (!File.Exists(packagePath))
        {
            return null;
        }

        if (Directory.Exists(packagePath))
        {
            return ReadManifestFromDirectory(packagePath);
        }

        if (IsZipArchive(packagePath))
        {
            return ReadManifestFromZip(packagePath);
        }

        return null;
    }

    public NexusPackageManifest? ReadManifestFromJsonFile(string manifestPath)
    {
        if (!File.Exists(manifestPath))
        {
            return null;
        }

        try
        {
            var json = File.ReadAllText(manifestPath);
            return JsonSerializer.Deserialize<NexusPackageManifest>(json, JsonOptions);
        }
        catch
        {
            return null;
        }
    }

    public NexusPackageManifest? ReadManifestFromDirectory(string directoryPath)
    {
        var manifestPath = Path.Combine(directoryPath, "manifest.json");
        return ReadManifestFromJsonFile(manifestPath);
    }

    private static NexusPackageManifest? ReadManifestFromZip(string packagePath)
    {
        try
        {
            using var archive = ZipFile.OpenRead(packagePath);
            var entry = archive.Entries.FirstOrDefault(e =>
                string.Equals(e.FullName, "manifest.json", StringComparison.OrdinalIgnoreCase) ||
                string.Equals(e.Name, "manifest.json", StringComparison.OrdinalIgnoreCase));

            if (entry is null)
            {
                return null;
            }

            using var stream = entry.Open();
            return JsonSerializer.Deserialize<NexusPackageManifest>(stream, JsonOptions);
        }
        catch
        {
            return null;
        }
    }

    private static bool IsZipArchive(string path)
    {
        try
        {
            using var stream = File.OpenRead(path);
            if (stream.Length < 4)
            {
                return false;
            }

            Span<byte> header = stackalloc byte[4];
            stream.ReadExactly(header);
            return header[0] == 0x50 && header[1] == 0x4B;
        }
        catch
        {
            return false;
        }
    }
}
