using System;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace MarcusLaw.OutlookAddin.Core.Json
{
    /// <summary>
    /// EspoCRM serializes date/datetime fields in one of these shapes:
    ///   * "YYYY-MM-DD HH:mm:ss"   (datetime, naive — server is UTC by convention)
    ///   * "YYYY-MM-DD"             (date only)
    ///   * ISO 8601 "YYYY-MM-DDTHH:mm:ss(+zz:zz|Z)"  (some custom builds)
    ///   * empty string / null
    /// .NET's default Utf8JsonReader.GetDateTimeOffset only accepts strict
    /// ISO 8601 with a timezone, so any of the first two forms throws and
    /// the whole list response fails to deserialize. This converter parses
    /// all four and assumes UTC when no offset is present.
    /// </summary>
    public sealed class FlexibleDateTimeOffsetConverter : JsonConverter<DateTimeOffset?>
    {
        private static readonly string[] Formats = new[]
        {
            "yyyy-MM-dd HH:mm:ss",
            "yyyy-MM-dd HH:mm",
            "yyyy-MM-ddTHH:mm:ss",
            "yyyy-MM-ddTHH:mm:ssK",
            "yyyy-MM-ddTHH:mm:ss.fffK",
            "yyyy-MM-dd"
        };

        public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            switch (reader.TokenType)
            {
                case JsonTokenType.Null:
                    return null;
                case JsonTokenType.String:
                    var s = reader.GetString();
                    if (string.IsNullOrWhiteSpace(s)) return null;
                    // Fast path — ISO with offset, what .NET accepts natively.
                    if (DateTimeOffset.TryParse(
                            s, CultureInfo.InvariantCulture,
                            DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
                            out var dto))
                    {
                        return dto;
                    }
                    foreach (var fmt in Formats)
                    {
                        if (DateTime.TryParseExact(
                                s, fmt, CultureInfo.InvariantCulture,
                                DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
                                out var dt))
                        {
                            return new DateTimeOffset(dt, TimeSpan.Zero);
                        }
                    }
                    return null;
                case JsonTokenType.Number:
                    // Some installs emit Unix epoch seconds.
                    if (reader.TryGetInt64(out var epoch))
                    {
                        return DateTimeOffset.FromUnixTimeSeconds(epoch);
                    }
                    return null;
                default:
                    reader.Skip();
                    return null;
            }
        }

        public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options)
        {
            if (value == null) writer.WriteNullValue();
            else writer.WriteStringValue(value.Value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
        }
    }
}
