using System.Reflection;
using System.Text.Json;
using Windows.ApplicationModel;
using YMhut.Box.Core.Updates;

namespace YMhut.Box.WinUI.Services;

public sealed class AppVersionService : IAppVersionService
{
    public AppVersionInfo GetCurrent()
    {
        try
        {
            var version = Package.Current.Id.Version;
            return new AppVersionInfo(
                Package.Current.DisplayName,
                $"{version.Major}.{version.Minor}.{version.Build}.{version.Revision}",
                true);
        }
        catch
        {
            if (TryReadVersionJson(out var jsonVersion))
            {
                return new AppVersionInfo("YMhut Box", jsonVersion, false);
            }

            var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
            var informationalVersion = assembly
                .GetCustomAttribute<AssemblyInformationalVersionAttribute>()
                ?.InformationalVersion;
            return new AppVersionInfo(
                "YMhut Box",
                informationalVersion ?? assembly.GetName().Version?.ToString() ?? "0.0.0.0",
                false);
        }
    }

    private static bool TryReadVersionJson(out string version)
    {
        if (TryReadEmbeddedVersionJson(out version))
        {
            return true;
        }

        foreach (var path in VersionFileCandidates())
        {
            try
            {
                if (!File.Exists(path))
                {
                    continue;
                }

                if (TryParseVersionJson(File.ReadAllText(path), out version))
                {
                    return true;
                }
            }
            catch
            {
            }
        }

        version = string.Empty;
        return false;
    }

    private static IEnumerable<string> VersionFileCandidates()
    {
        yield return Path.Combine(AppContext.BaseDirectory, "version.json");

        var directory = new DirectoryInfo(AppContext.BaseDirectory);
        while (directory is not null)
        {
            yield return Path.Combine(directory.FullName, "version.json");
            directory = directory.Parent;
        }
    }

    private static bool TryReadEmbeddedVersionJson(out string version)
    {
        version = string.Empty;
        try
        {
            var assembly = Assembly.GetExecutingAssembly();
            using var stream = assembly.GetManifestResourceStream("YMhut.Box.WinUI.version.json");
            if (stream is null)
            {
                return false;
            }

            using var reader = new StreamReader(stream);
            return TryParseVersionJson(reader.ReadToEnd(), out version);
        }
        catch
        {
            version = string.Empty;
            return false;
        }
    }

    private static bool TryParseVersionJson(string json, out string version)
    {
        using var document = JsonDocument.Parse(json);
        var root = document.RootElement;
        var baseVersion = root.TryGetProperty("version", out var versionElement)
            ? versionElement.GetString()
            : null;
        var build = root.TryGetProperty("build", out var buildElement)
            ? buildElement.GetString()
            : null;
        if (string.IsNullOrWhiteSpace(baseVersion))
        {
            version = string.Empty;
            return false;
        }

        version = UpdateVersionComparer.NormalizeVersion(baseVersion, build);
        if (string.IsNullOrWhiteSpace(version))
        {
            version = baseVersion.Trim();
        }

        return true;
    }
}
