using System.Collections;
using System.Collections.Specialized;
using PkmnLib.Static.Utils;

namespace PkmnLib.Dynamic.AI.Explicit;

public class FunctionHandlerDictionary<TValue> : IDictionary<StringKey, TValue>, IReadOnlyDictionary<StringKey, TValue>
    where TValue : class
{
    private readonly OrderedDictionary _backingDictionary = new();

    /// <inheritdoc />
    public IEnumerator<KeyValuePair<StringKey, TValue>> GetEnumerator() => _backingDictionary.Cast<DictionaryEntry>()
        .Select(entry => new KeyValuePair<StringKey, TValue>((StringKey)entry.Key, (TValue)entry.Value))
        .GetEnumerator();

    /// <inheritdoc />
    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    /// <inheritdoc />
    public void Add(KeyValuePair<StringKey, TValue> item) => _backingDictionary.Add(item.Key, item.Value);

    /// <inheritdoc />
    public void Clear() => _backingDictionary.Clear();

    /// <inheritdoc />
    public bool Contains(KeyValuePair<StringKey, TValue> item)
    {
        if (_backingDictionary.Contains(item.Key))
        {
            var value = _backingDictionary[item.Key];
            return EqualityComparer<TValue>.Default.Equals((TValue)value, item.Value);
        }
        return false;
    }

    /// <inheritdoc />
    public void CopyTo(KeyValuePair<StringKey, TValue>[] array, int arrayIndex)
    {
        if (array == null)
            throw new ArgumentNullException(nameof(array));
        if (arrayIndex < 0 || arrayIndex + Count > array.Length)
            throw new ArgumentOutOfRangeException(nameof(arrayIndex));

        foreach (var item in this)
        {
            array[arrayIndex++] = item;
        }
    }

    /// <inheritdoc />
    public bool Remove(KeyValuePair<StringKey, TValue> item)
    {
        if (_backingDictionary.Contains(item.Key))
        {
            var value = _backingDictionary[item.Key];
            if (EqualityComparer<TValue>.Default.Equals((TValue)value, item.Value))
            {
                _backingDictionary.Remove(item.Key);
                return true;
            }
        }
        return false;
    }

    /// <inheritdoc cref="IReadOnlyDictionary{StringKey, TValue}.Count" />
    public int Count => _backingDictionary.Count;

    /// <inheritdoc />
    public bool IsReadOnly => false;

    /// <inheritdoc />
    public void Add(StringKey key, TValue value) => _backingDictionary.Add(key, value);

    /// <inheritdoc cref="IReadOnlyDictionary{StringKey, TValue}.ContainsKey" />
    public bool ContainsKey(StringKey key) => _backingDictionary.Contains(key);

    /// <inheritdoc />
    public bool Remove(StringKey key)
    {
        if (!_backingDictionary.Contains(key))
            return false;
        _backingDictionary.Remove(key);
        return true;
    }

    /// <inheritdoc />
    public bool TryGetValue(StringKey key, out TValue value)
    {
        if (_backingDictionary.Contains(key))
        {
            value = (TValue)_backingDictionary[key];
            return true;
        }
        value = null!;
        return false;
    }

    /// <inheritdoc />
    public TValue this[StringKey key]
    {
        get => (TValue)_backingDictionary[key];
        set => _backingDictionary[key] = value;
    }

    /// <inheritdoc />
    IEnumerable<StringKey> IReadOnlyDictionary<StringKey, TValue>.Keys => Keys;

    /// <inheritdoc />
    IEnumerable<TValue> IReadOnlyDictionary<StringKey, TValue>.Values => Values;

    /// <inheritdoc />
    public ICollection<StringKey> Keys => _backingDictionary.Keys.Cast<StringKey>().ToList();

    /// <inheritdoc />
    public ICollection<TValue> Values => _backingDictionary.Values.Cast<TValue>().ToList();
}