using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;

namespace Quadarax.Foundation.Core.Json.Extensions
{
    public static class JsonDocumentExt
    {

        public static IDictionary<string, DiffOccurrence> Diff(this JsonDocument src, JsonDocument trg)
        {
            if (src == null) throw new ArgumentNullException(nameof(src));
            if (trg == null) throw new ArgumentNullException(nameof(trg));

            var result = new Dictionary<string, DiffOccurrence>();

            var srcEnumerator = src.RootElement.EnumerateObject();
            var trgEnumerator = trg.RootElement.EnumerateObject();
            while (srcEnumerator.MoveNext())
            {
                var srcProperty = srcEnumerator.Current;
                var trgProperty = Find(srcProperty.Name, trgEnumerator.GetEnumerator());

                if (trgProperty == null)
                    result.Add(srcProperty.Name, new DiffOccurrence(srcProperty.Name, DiffTypeEnum.TrgMissing, srcProperty.Value.GetRawText(), null));
                else if (!string.Equals(srcProperty.Value.GetRawText(), trgProperty.Value.Value.GetRawText(),StringComparison.InvariantCultureIgnoreCase))
                        result.Add(srcProperty.Name, new DiffOccurrence(srcProperty.Name, DiffTypeEnum.Different, srcProperty.Value.GetRawText(), trgProperty.Value.Value.GetRawText()));
            }

            while (trgEnumerator.MoveNext())
            {
                var trgProperty = trgEnumerator.Current;
                var srcProperty = Find(trgProperty.Name, srcEnumerator.GetEnumerator());
                if (srcProperty == null)
                {
                    result.Add(trgProperty.Name, new DiffOccurrence(trgProperty.Name, DiffTypeEnum.SrcMissing, null, trgProperty.Value.ToString()));
                }
            }

            return result;
        }

        private static JsonProperty? Find(string name, IEnumerator<JsonProperty> enumerator)
        {
            while (enumerator.MoveNext())
            {
                var property = enumerator.Current;
                if (property.Name.Equals(name,StringComparison.InvariantCultureIgnoreCase))
                    return property;
            }
            return null;
        }

        #region *** Nested classes ***

        public class DiffOccurrence
        {
            public string Name { get; private set; }
            public DiffTypeEnum DiffType { get; private set; }
            public string? SrcValue { get; private set; }
            public string? TrgValue { get; private set; }

            public DiffOccurrence(string name, DiffTypeEnum diffType, string? srcValue, string? trgValue)
            {
                if (string.IsNullOrEmpty(name))
                    throw new ArgumentNullException(nameof(name));
                Name = name;
                DiffType = diffType;
                SrcValue = srcValue;
                TrgValue = trgValue;
            }
            
            public override string ToString()
            {
                var sb = new StringBuilder();
                sb.Append("Name=").Append(Name).Append(";DiffType=").Append(DiffType);
                return sb.ToString();
            }
        }

        public enum DiffTypeEnum
        {
            SrcMissing,
            TrgMissing,
            Different
        }

        #endregion
    }
}
