using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.сеть;
using Mlaumcherb.Client.Avalonia.холопы;
using Newtonsoft.Json.Linq;

namespace Mlaumcherb.Client.Avalonia.классы;

public class InstalledGameVersionProps : IComparable<InstalledGameVersionProps>, IEquatable<InstalledGameVersionProps>
{
    [JsonRequired] public string Id { get; }
    public GameVersionCatalogProps? CatalogProps { get; set; }

    public RemoteVersionDescriptorProps? DescriptorProps { get; set; }
    

    private bool _isInstalled;
    [JsonIgnore]
    public bool IsInstalled
    {
        get => _isInstalled;
        set
        {
            _isInstalled = value;
            StatusChanged?.Invoke(value);
        }
    }

    [JsonIgnore] public IOPath DescriptorPath { get; }
    
    public event Action<bool>? StatusChanged;

    public InstalledGameVersionProps(string id, GameVersionCatalogProps? catalogProps, RemoteVersionDescriptorProps? descriptorProps)
    {
        this.Id = id;
        this.CatalogProps = catalogProps;
        this.DescriptorProps = descriptorProps;
        DescriptorPath = PathHelper.GetVersionDescriptorPath(id);
        IsInstalled = File.Exists(PathHelper.GetVersionJarFilePath(id));
    }

    public async Task<GameVersion> LoadDescriptor(bool checkForUpdates)
    {
        // check if DescriptorProps had been changed on on remote version catalog server
        if (checkForUpdates && CatalogProps is not null && DescriptorProps is not null)
        {
            var remoteCatalog = await NetworkHelper.DownloadStringAndDeserialize<GameVersionCatalog>(CatalogProps.url);

            DescriptorProps = remoteCatalog.versions.Find(remoteProps => remoteProps.id == Id);
            if(DescriptorProps is null)
                LauncherApp.Logger.LogWarn(nameof(InstalledGameVersionProps), 
                    $"Game version '{Id}' was deleted from remote catalog '{CatalogProps.name}'."
                    + "It is kept locally, but can't be updated anymore.");
        }
        
        // Descriptor is downloaded here
        if (!HashHelper.CheckFileSHA1(DescriptorPath, DescriptorProps?.sha1, checkForUpdates))
        {
            if (DescriptorProps is null)
                throw new NullReferenceException(
                    $"can't download game version descriptor '{Id}', because RemoteDescriptor is null");
            // download descriptor
            await NetworkHelper.DownloadFile(DescriptorProps.url, DescriptorPath);
        }

        string descriptorText = File.ReadAllText(DescriptorPath);
        JObject descriptorRaw =  JObject.Parse(descriptorText);
        
        // Descriptors can inherit from other descriptors.
        // For example, timerix-anarx-2 inherits from 1.12.2-forge-14.23.5.2860, which inherits from 1.12.2
        while (descriptorRaw.TryGetValue("inheritsFrom", out var v))
        {
            string parentDescriptorId = v.Value<string>() 
                ?? throw new Exception("inheritsFrom is null");
            LauncherApp.Logger.LogInfo(Id, $"merging descriptor '{parentDescriptorId}' with '{Id}'");
            
            IOPath parentDescriptorPath = PathHelper.GetVersionDescriptorPath(parentDescriptorId);
            if (!File.Exists(parentDescriptorPath))
                throw new Exception($"Версия '{Id} требует установить версию '{parentDescriptorId}'");
            JObject parentDescriptorRaw = JObject.Parse(File.ReadAllText(parentDescriptorPath));
            
            // Remove `inheritsFrom: parentDescriptor.Id` to prevent overriding of `parentDescriptor.inheritsFrom`
            descriptorRaw.Remove("inheritsFrom");
            // Merge child descriptor into its parent.
            // Child descriptor values override values of parent.
            // Array values are merged together.
            parentDescriptorRaw.Merge(descriptorRaw,
                new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat });
            descriptorRaw = parentDescriptorRaw;
        }

        var descriptor = descriptorRaw.ToObject<GameVersionDescriptor>()
                      ?? throw new Exception($"can't parse descriptor file '{DescriptorPath}'");
        
        return new GameVersion(this, descriptor);
    }
    
    public void Delete()
    {
        if(!IsInstalled)
            return;
        
        LauncherApp.Logger.LogInfo(Id, "Deleting files...");
        var verdir = PathHelper.GetVersionDir(Id);
        if(Directory.Exists(verdir))
            Directory.Delete(verdir);
        LauncherApp.Logger.LogInfo(Id, "Files deleted");
        IsInstalled = false;
    }
    
    public override string ToString() => Id;

    public override int GetHashCode() => Id.GetHashCode();

    public int CompareTo(InstalledGameVersionProps? other)
    {
        if (ReferenceEquals(this, other)) 
            return 0;
        if (other is null) 
            return 1;
        
        return String.Compare(Id, other.Id, StringComparison.InvariantCulture);
    }

    public bool Equals(InstalledGameVersionProps? other)
    {
        if (other is null) return false;
        if (ReferenceEquals(this, other)) return true;
        return Id == other.Id;
    }

    public override bool Equals(object? obj)
    {
        if (obj is InstalledGameVersionProps other) return Equals(other);
        if (ReferenceEquals(this, obj)) return true;
        return false;
    }
}
