using System;
using System.Collections.Generic;
using System.Linq;
using IPA.JsonConverters;
using IPA.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SemVer;
using Version = SemVer.Version;

namespace IPA.Updating.BeatMods
{
#if NET4
    class ApiEndpoint
    {
        public const string BeatModBase = "https://beatmods.com";
        public const string ApiBase = BeatModBase + "/api/v1/mod";
        public const string GetModInfoEndpoint = "?name={0}&version={1}";
        public const string GetModsByName = "?name={0}";

        class HexArrayConverter : JsonConverter
        {
            public override bool CanConvert(Type objectType)
            {
                return objectType == typeof(byte[]);
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                if (reader.TokenType == JsonToken.Null)
                {
                    return null;
                }
                if (reader.TokenType == JsonToken.String)
                {
                    try
                    {
                        return Utils.StringToByteArray((string)reader.Value);
                    }
                    catch (Exception ex)
                    {
                        throw new Exception($"Error parsing version string: {reader.Value}", ex);
                    }
                }
                throw new Exception(
                    $"Unexpected token or value when parsing hex string. Token: {reader.TokenType}, Value: {reader.Value}");
            }

            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                if (value == null)
                {
                    writer.WriteNull();
                }
                else
                {
                    if (!(value is byte[]))
                    {
                        throw new JsonSerializationException("Expected byte[] object value");
                    }
                    writer.WriteValue(Utils.ByteArrayToString((byte[]) value));
                }
            }
        }
        class ModMultiformatJsonConverter : JsonConverter<Mod>
        {
            public override Mod ReadJson(JsonReader reader, Type objectType, Mod existingValue, bool hasExistingValue, JsonSerializer serializer)
            {
                if (reader.TokenType == JsonToken.String)
                    return new Mod { Id = reader.Value as string, IsIdReference = true };
                else
                {
                    if (reader.TokenType != JsonToken.StartObject)
                        return null;

                    return serializer.Deserialize<Mod>(reader);
                }
            }

            public override void WriteJson(JsonWriter writer, Mod value, JsonSerializer serializer) => serializer.Serialize(writer, value);
        }

        [Serializable]
        public class Mod
        {
#pragma warning disable CS0649
            /// <summary>
            /// Will be a useless string of characters. Do not use.
            /// </summary>
            [JsonProperty("_id")]
            public string Id;

            [JsonIgnore]
            public bool IsIdReference = false;

            [JsonProperty("required")]
            public bool Required;

            [JsonProperty("name")]
            public string Name;

            [JsonProperty("version"),
             JsonConverter(typeof(SemverVersionConverter))]
            public Version Version;

            [JsonProperty("gameVersion"),
             JsonConverter(typeof(AlmostVersionConverter))]
            public AlmostVersion GameVersion;

            [Serializable]
            public class AuthorType
            {
                [JsonProperty("username")]
                public string Name;
                [JsonProperty("_id")]
                public string Id;

                public override string ToString() => Name;
            }

            [JsonProperty("author")]
            public AuthorType Author;

            [JsonProperty("status")]
            public string Status;
            public const string ApprovedStatus = "approved";

            [JsonProperty("description")]
            public string Description;

            [JsonProperty("category")]
            public string Category;

            [JsonProperty("link")]
            public Uri Link;
            
#pragma warning restore CS0649

            [Serializable]
            public class DownloadsObject
            {
                public const string TypeUniversal = "universal";
                public const string TypeSteam = "steam";
                public const string TypeOculus = "oculus";

                [JsonProperty("type")]
                public string Type;

                [JsonProperty("url")]
                public string Path;

                [Serializable]
                public class HashObject
                {
                    [JsonProperty("hash"), JsonConverter(typeof(HexArrayConverter))]
                    public byte[] Hash;

                    [JsonProperty("file")]
                    public string File;
                }

                /// <summary>
                /// Hashes stored are MD5
                /// </summary>
                [JsonProperty("hashMd5")]
                public HashObject[] Hashes;
            }

            [JsonProperty("downloads")]
            public DownloadsObject[] Downloads;

            [JsonProperty("dependencies", ItemConverterType = typeof(ModMultiformatJsonConverter))]
            public Mod[] Dependencies;

            public override string ToString()
            {
                return $"{{\"{Name}\"v{Version} by {Author} files for {string.Join(", ", Downloads.Select(d => d.Type))}}}";
            }
        }

    }
#endif
}
