using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using AyCode.Core.Extensions;
using System.Threading;

namespace AyCode.Core.Helpers
{
    public interface IAcObservableCollection
    {
        public void AddRange(IEnumerable other);
        public void Replace(IEnumerable other);
        public void RemoveRange(IEnumerable other);
        public void Synchronize(NotifyCollectionChangedEventArgs args);

        /// <summary>
        /// Populates/merges data from object source while suppressing per-item change events.
        /// Fires a single Reset event at the end.
        /// </summary>
        void PopulateFrom(object source);

        /// <summary>
        /// Populates/merges data from json while suppressing per-item change events.
        /// Fires a single Reset event at the end.
        /// </summary>
        void PopulateFromJson(string json, bool clearAll = false);

        /// <summary>
        /// Begins a batch update operation. All notifications are suppressed until EndUpdate is called.
        /// Supports nested calls - only the outermost EndUpdate triggers the notification.
        /// </summary>
        public void BeginUpdate();

        /// <summary>
        /// Ends a batch update operation. Triggers a single Reset notification if this is the outermost call.
        /// </summary>
        public void EndUpdate();

        /// <summary>
        /// Forces a Reset notification to refresh bound UI controls.
        /// </summary>
        public void NotifyReset();

        /// <summary>
        /// Returns true if currently in a batch update operation.
        /// </summary>
        public bool IsUpdating { get; }
    }

    public interface IAcObservableCollection<T> : IAcObservableCollection
    {
        public void Replace(IEnumerable<T> other);
        public void Sort(IComparer<T> comparer);
        public void SortAndReplace(IEnumerable<T> other, IComparer<T> comparer);
    }

    /// <summary>
    /// Thread-safe ObservableCollection with batch update support.
    /// All public methods are synchronized using a lock.
    /// </summary>
    public class AcObservableCollection<T> : ObservableCollection<T>, IAcObservableCollection<T>
    {
        private readonly object _syncRoot = new();
        private int _updateCount;
        private SynchronizationContext? _synchronizationContext;

        /// <summary>
        /// Returns true if currently in a batch update operation.
        /// </summary>
        public bool IsUpdating
        {
            get
            {
                lock (_syncRoot)
                {
                    return _updateCount > 0;
                }
            }
        }

        /// <summary>
        /// Gets the synchronization object for external locking scenarios.
        /// </summary>
        public object SyncRoot => _syncRoot;

        public AcObservableCollection() : base()
        {
            CaptureSynchronizationContext();
        }

        public AcObservableCollection(List<T> list) : base(list)
        {
            CaptureSynchronizationContext();
        }

        public AcObservableCollection(IEnumerable<T> collection) : base(collection)
        {
            CaptureSynchronizationContext();
        }

        /// <summary>
        /// Captures the current SynchronizationContext for UI thread marshalling.
        /// Should be called from the UI thread during construction.
        /// </summary>
        private void CaptureSynchronizationContext()
        {
            _synchronizationContext = SynchronizationContext.Current;
        }

        /// <summary>
        /// Allows setting a custom SynchronizationContext (e.g., from a Blazor component).
        /// </summary>
        public void SetSynchronizationContext(SynchronizationContext? context)
        {
            _synchronizationContext = context;
        }

        public void BeginUpdate()
        {
            lock (_syncRoot)
            {
                _updateCount++;
            }
        }

        public void EndUpdate()
        {
            bool shouldNotify;
            lock (_syncRoot)
            {
                if (_updateCount <= 0) return;
                _updateCount--;
                shouldNotify = _updateCount == 0;
            }

            if (shouldNotify) NotifyReset();
        }

        public void NotifyReset()
        {
            var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            var propertyArgs = new PropertyChangedEventArgs(nameof(Count));

            // Ha van SynchronizationContext és nem azon a szálon vagyunk, marshal-oljuk a hívást
            if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
            {
                _synchronizationContext.Post(_ =>
                {
                    OnCollectionChanged(args);
                    OnPropertyChanged(propertyArgs);
                }, null);
            }
            else
            {
                OnCollectionChanged(args);
                OnPropertyChanged(propertyArgs);
            }
        }

        public new void Add(T item)
        {
            lock (_syncRoot)
            {
                base.Add(item);
            }
        }

        public new bool Remove(T item)
        {
            lock (_syncRoot)
            {
                return base.Remove(item);
            }
        }

        public new void Insert(int index, T item)
        {
            lock (_syncRoot)
            {
                base.Insert(index, item);
            }
        }

        public new void RemoveAt(int index)
        {
            lock (_syncRoot)
            {
                base.RemoveAt(index);
            }
        }

        public new void Clear()
        {
            lock (_syncRoot)
            {
                base.Clear();
            }
        }

        public new void Move(int oldIndex, int newIndex)
        {
            lock (_syncRoot)
            {
                base.Move(oldIndex, newIndex);
            }
        }

        public new T this[int index]
        {
            get
            {
                lock (_syncRoot)
                {
                    return base[index];
                }
            }
            set
            {
                lock (_syncRoot)
                {
                    base[index] = value;
                }
            }
        }

        public new int Count
        {
            get
            {
                lock (_syncRoot)
                {
                    return base.Count;
                }
            }
        }

        public new bool Contains(T item)
        {
            lock (_syncRoot)
            {
                return base.Contains(item);
            }
        }

        public new int IndexOf(T item)
        {
            lock (_syncRoot)
            {
                return base.IndexOf(item);
            }
        }

        public new void CopyTo(T[] array, int arrayIndex)
        {
            lock (_syncRoot)
            {
                base.CopyTo(array, arrayIndex);
            }
        }

        /// <summary>
        /// Returns a snapshot copy of the collection for safe enumeration.
        /// </summary>
        public List<T> ToList()
        {
            lock (_syncRoot)
            {
                return [.. this.Items];
            }
        }

        public void Replace(IEnumerable<T> other)
        {
            BeginUpdate();
            try
            {
                lock (_syncRoot)
                {
                    base.Clear();
                    foreach (var item in other) base.Add(item);
                }
            }
            finally
            {
                EndUpdate();
            }
        }

        public void Replace(IEnumerable other)
        {
            BeginUpdate();
            try
            {
                lock (_syncRoot)
                {
                    base.Clear();
                    foreach (T item in other) base.Add(item);
                }
            }
            finally
            {
                EndUpdate();
            }
        }

        public void AddRange(IEnumerable other)
        {
            BeginUpdate();
            try
            {
                lock (_syncRoot)
                {
                    foreach (var item in other)
                    {
                        if (item is T tItem) base.Add(tItem);
                    }
                }
            }
            finally
            {
                EndUpdate();
            }
        }

        public void RemoveRange(IEnumerable other)
        {
            BeginUpdate();
            try
            {
                lock (_syncRoot)
                {
                    foreach (var item in other)
                    {
                        if (item is T tItem) base.Remove(tItem);
                    }
                }
            }
            finally
            {
                EndUpdate();
            }
        }

        public void PopulateFrom(object source)
        {
            switch (source)
            {
                case IEnumerable<T> typedSource:
                    Replace(typedSource);
                    break;
                case IEnumerable enumerable:
                    Replace(enumerable);
                    break;
            }
        }

        public void PopulateFromJson(string json, bool clearAll = false)
        {
            BeginUpdate();
            try
            {
                lock (_syncRoot)
                {
                    if (clearAll) base.Clear();
                    json.JsonTo(this.Items);
                }
            }
            finally
            {
                EndUpdate();
            }
        }

        public void SortAndReplace(IEnumerable<T> other, IComparer<T> comparer)
        {
            var values = new List<T>(other);
            values.Sort(comparer);
            Replace(values);
        }

        public void Sort(IComparer<T> comparer)
        {
            List<T> values;
            lock (_syncRoot)
            {
                values = new List<T>(this.Items);
            }
            values.Sort(comparer);
            Replace(values);
        }

        public void Synchronize(NotifyCollectionChangedEventArgs args)
        {
            switch (args.Action)
            {
                case NotifyCollectionChangedAction.Add when args.NewItems != null:
                    AddRange(args.NewItems);
                    break;
                case NotifyCollectionChangedAction.Remove when args.OldItems != null:
                    RemoveRange(args.OldItems);
                    break;
                case NotifyCollectionChangedAction.Reset:
                    Clear();
                    break;
                case NotifyCollectionChangedAction.Replace:
                case NotifyCollectionChangedAction.Move:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (IsUpdating) return;

            try
            {
                base.OnPropertyChanged(e);
            }
            catch (ObjectDisposedException)
            {
                // A feliratkozott komponens már Disposed - biztonságosan figyelmen kívül hagyjuk
            }
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (IsUpdating) return;

            try
            {
                base.OnCollectionChanged(e);
            }
            catch (ObjectDisposedException)
            {
                // A feliratkozott komponens már Disposed - biztonságosan figyelmen kívül hagyjuk
            }
        }
    }
}