using System;
using System.Reflection;
using Newtonsoft.Json;

namespace BTCPayServer.JsonConverters
{
    class DateTimeMilliJsonConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return typeof(DateTime).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()) ||
                   typeof(DateTimeOffset).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo()) ||
                   typeof(DateTimeOffset?).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
        }

        static readonly DateTimeOffset unixRef = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            if (reader.Value == null)
                return null;
            if (reader.TokenType == JsonToken.Null)
                return null;
            var result = UnixTimeToDateTime((ulong)(long)reader.Value);
            if (objectType == typeof(DateTime))
                return result.UtcDateTime;
            return result;
        }

        private DateTimeOffset UnixTimeToDateTime(ulong value)
        {
            var v = (long)value;
            if (v < 0)
                throw new FormatException("Invalid datetime (less than 1/1/1970)");
            return unixRef + TimeSpan.FromMilliseconds(v);
        }
        private long DateTimeToUnixTime(in DateTime time)
        {
            var date = ((DateTimeOffset)time).ToUniversalTime();
            long v = (long)(date - unixRef).TotalMilliseconds;
            if (v < 0)
                throw new FormatException("Invalid datetime (less than 1/1/1970)");
            return v;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            DateTime time;
            if (value is DateTime)
                time = (DateTime)value;
            else
                time = ((DateTimeOffset)value).UtcDateTime;

            if (time < UnixTimeToDateTime(0))
                time = UnixTimeToDateTime(0).UtcDateTime;
            writer.WriteValue(DateTimeToUnixTime(time));
        }


    }
}