using MongoDB.Bson;
using Newtonsoft.Json;

namespace adas_core.Domain.Utils;

/// <summary>
/// Provides a JSON converter for serializing and deserializing <see cref="ObjectId"/> values.
/// </summary>
/// <remarks>
/// Inherits from <see cref="JsonConverter"/> to customize JSON representation for the type it targets.
/// </remarks>
public class ObjectIdConverter : JsonConverter
{
    /// <summary>
    /// Serializes an <see cref="ObjectId"/> instance to JSON by writing its string representation; if the value is not an <see cref="ObjectId"/>, writes a JSON null instead.
    /// </summary>
    /// <param name="writer">The <see cref="JsonWriter"/> used to emit the JSON output.</param>
    /// <param name="value">The value to serialize, expected to be an <see cref="ObjectId"/> instance.</param>
    /// <param name="serializer">The <see cref="JsonSerializer"/> invoking this converter.</param>
    public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            if (value is ObjectId objectId)
                // Serializa el ObjectId como un string
                writer.WriteValue(objectId.ToString());
            else
                writer.WriteNull();
        }

    /// <summary>
    /// Reads a JSON value and converts it to an <see cref="ObjectId"/> instance. When the token is a non-empty string different from the default ObjectId representation and parses successfully, the parsed value is returned; otherwise the method falls back to <see cref="ObjectId.Empty"/> for non-nullable target types or <c>null</c> for nullable target types.
    /// </summary>
    /// <param name="reader">The JSON reader positioned on the value to deserialize.</param>
    /// <param name="objectType">The target type expected by the deserializer, used to decide between returning <see cref="ObjectId.Empty"/> or <c>null</c> when the value cannot be parsed.</param>
    /// <param name="existingValue">The existing value being populated, passed through from the deserializer.</param>
    /// <param name="serializer">The JSON serializer invoking this converter.</param>
    /// <returns>An <see cref="ObjectId"/> instance when the value can be parsed or when the target type is non-nullable; <c>null</c> when the target type is nullable and no valid value is found.</returns>
    public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue,
            JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.String)
            {
                var stringValue = reader.Value as string;
                // Verifica si el valor es el valor predeterminado de ObjectId
                if (!string.IsNullOrEmpty(stringValue) &&
                    stringValue != "000000000000000000000000" &&
                    ObjectId.TryParse(stringValue, out var oid))
                    return oid;
            }
    
            // Nullables reciben null, no-null vuelven Empty
            if (objectType == typeof(ObjectId))
                return ObjectId.Empty;
            // Retorna null o ObjectId.Empty para manejar el valor predeterminado de ObjectId
            return null;
        }

    /// <summary>
    /// Determines whether the converter can convert the specified type by checking if the type is assignable from <see cref="ObjectId"/>.
    /// </summary>
    /// <param name="objectType">The type to evaluate for compatibility with the converter.</param>
    /// <returns><see langword="true"/> if <paramref name="objectType"/> can be assigned from <see cref="ObjectId"/>; otherwise, <see langword="false"/>.</returns>
    public override bool CanConvert(Type objectType)
        {
            return typeof(ObjectId).IsAssignableFrom(objectType);
        }
}