namespace ByteBard.AsyncAPI.Bindings.Pulsar
{
    using System;
    using System.Collections.Generic;
    using ByteBard.AsyncAPI.Models;
    using ByteBard.AsyncAPI.Models.Bindings.Pulsar;
    using ByteBard.AsyncAPI.Readers.ParseNodes;
    using ByteBard.AsyncAPI.Writers;

    public class PulsarChannelBinding : ChannelBinding<PulsarChannelBinding>
    {
        /// <summary>
        /// The namespace associated with the topic.
        /// </summary>
        public string Namespace { get; set; }

        /// <summary>
        /// persistence of the topic in Pulsar persistent or non-persistent.
        /// </summary>
        public Persistence Persistence { get; set; }

        /// <summary>
        /// Topic compaction threshold given in bytes.
        /// </summary>
        public int? Compaction { get; set; }

        /// <summary>
        /// A list of clusters the topic is replicated to.
        /// </summary>
        public IEnumerable<string> GeoReplication { get; set; }

        /// <summary>
        /// Topic retention policy.
        /// </summary>
        public RetentionDefinition Retention { get; set; }

        /// <summary>
        /// Message Time-to-live in seconds.
        /// </summary>
        public int? TTL { get; set; }

        /// <summary>
        /// When Message deduplication is enabled, it ensures that each message produced on Pulsar topics is persisted to disk only once.
        /// </summary>
        public bool? Deduplication { get; set; }

        public override string BindingKey => "pulsar";

        public override void SerializeV2(IAsyncApiWriter writer)
        {
            this.SerializeV3(writer);
        }

        public override void SerializeV3(IAsyncApiWriter writer)
        {
            this.SerializeProperties(writer);
        }

        public override void SerializeProperties(IAsyncApiWriter writer)
        {
            if (writer is null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            writer.WriteStartObject();
            writer.WriteRequiredProperty(AsyncApiConstants.Namespace, this.Namespace);
            writer.WriteRequiredProperty(AsyncApiConstants.Persistence, this.Persistence.GetDisplayName());
            writer.WriteOptionalProperty<int>(AsyncApiConstants.Compaction, this.Compaction);
            writer.WriteOptionalCollection(AsyncApiConstants.GeoReplication, this.GeoReplication, (v, s) => v.WriteValue(s));
            writer.WriteOptionalObject(AsyncApiConstants.Retention, this.Retention, (w, r) => r.Serialize(w));
            writer.WriteOptionalProperty<int>(AsyncApiConstants.TTL, this.TTL);
            writer.WriteOptionalProperty(AsyncApiConstants.Deduplication, this.Deduplication);
            writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion);
            writer.WriteExtensions(this.Extensions);
            writer.WriteEndObject();
        }

        protected override FixedFieldMap<PulsarChannelBinding> FixedFieldMap => new()
        {
            { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } },
            { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } },
            { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName<Persistence>(); } },
            { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } },
            { "retention", (a, n) => { a.Retention = n.ParseMap(this.pulsarServerBindingRetentionFixedFields); } },
            { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } },
            { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } },
            { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } },
        };

        private FixedFieldMap<RetentionDefinition> pulsarServerBindingRetentionFixedFields = new()
        {
            { "time", (a, n) => { a.Time = n.GetIntegerValue(); } },
            { "size", (a, n) => { a.Size = n.GetIntegerValue(); } },
        };
    }
}
