using System.Collections;
using AyCode.Core.Enums;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Services.Server.Tests.SignalRs.SignalRDatasources;

public abstract partial class SignalRDataSourceTestBase<TDataSource, TIList>
{
    #region Collection Operations Tests

    [TestMethod]
    public virtual async Task Count_ReturnsCorrectValue()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        Assert.AreEqual(3, dataSource.Count);
    }

    [TestMethod]
    public virtual void Clear_RemovesAllItems()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Item1" });
        dataSource.Add(new TestOrderItem_All_True { Id = 2, ProductName = "Item2" });
        dataSource.Clear();

        Assert.AreEqual(0, dataSource.Count);
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);
    }

    [TestMethod]
    public virtual void Clear_WithoutClearingTracking_PreservesTracking()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Item1" });
        dataSource.Clear(clearChangeTracking: false);

        Assert.AreEqual(0, dataSource.Count);
        Assert.AreEqual(1, dataSource.GetTrackingItems().Count);
    }

    [TestMethod]
    public virtual async Task Contains_ReturnsTrue_WhenItemExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        Assert.IsTrue(dataSource.Contains(dataSource[0]));
    }

    [TestMethod]
    public virtual void Contains_ReturnsFalse_WhenItemNotExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsFalse(dataSource.Contains(new TestOrderItem_All_True { Id = 9999 }));
    }

    [TestMethod]
    public virtual async Task IndexOf_ReturnsCorrectIndex()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        Assert.AreEqual(0, dataSource.IndexOf(dataSource[0]));
        Assert.AreEqual(1, dataSource.IndexOf(dataSource[1]));
        Assert.AreEqual(2, dataSource.IndexOf(dataSource[2]));
    }

    [TestMethod]
    public virtual void IndexOf_ById_ReturnsCorrectIndex()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 5, ProductName = "Item5" });
        Assert.AreEqual(0, dataSource.IndexOf(5));
    }

    [TestMethod]
    public virtual void TryGetIndex_ReturnsTrue_WhenExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 10, ProductName = "Test" });
        var result = dataSource.TryGetIndex(10, out var index);

        Assert.IsTrue(result);
        Assert.AreEqual(0, index);
    }

    [TestMethod]
    public virtual async Task TryGetValue_ReturnsItem_WhenExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        await dataSource.LoadDataSource();
        var result = dataSource.TryGetValue(1, out var item);

        Assert.IsTrue(result);
        Assert.IsNotNull(item);
        Assert.AreEqual("Product A", item.ProductName);
    }

    [TestMethod]
    public virtual void TryGetValue_ReturnsFalse_WhenNotExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var result = dataSource.TryGetValue(9999, out var item);

        Assert.IsFalse(result);
        Assert.IsNull(item);
    }

    [TestMethod]
    public virtual async Task CopyTo_CopiesAllItems()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        await dataSource.LoadDataSource();
        var array = new TestOrderItem_All_True[3];
        dataSource.CopyTo(array);

        Assert.AreEqual("Product A", array[0].ProductName);
        Assert.AreEqual("Product B", array[1].ProductName);
        Assert.AreEqual("Product C", array[2].ProductName);
    }

    [TestMethod]
    public virtual async Task GetEnumerator_EnumeratesAllItems()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        var count = dataSource.Count();

        Assert.AreEqual(3, count);
    }

    [TestMethod]
    public virtual async Task AsReadOnly_ReturnsReadOnlyCollection()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        await dataSource.LoadDataSource();
        var readOnly = dataSource.AsReadOnly();

        Assert.AreEqual(3, readOnly.Count);
        Assert.IsInstanceOfType(readOnly, typeof(System.Collections.ObjectModel.ReadOnlyCollection<TestOrderItem_All_True>));
    }

    #endregion

    #region Working Reference List Tests

    [TestMethod]
    public virtual async Task SetWorkingReferenceList_SetsNewInnerList()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        var externalList = Activator.CreateInstance<TIList>();
        dataSource.SetWorkingReferenceList(externalList);

        Assert.IsTrue(dataSource.HasWorkingReferenceList);
        Assert.AreEqual(3, dataSource.Count);
    }

    [TestMethod]
    public virtual async Task GetReferenceInnerList_ReturnsInnerList()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        await dataSource.LoadDataSource();
        var innerList = dataSource.GetReferenceInnerList();

        Assert.IsNotNull(innerList);
        Assert.AreEqual(3, innerList.Count);
    }

    #endregion

    #region Sync State Tests

    [TestMethod]
    public virtual void IsSyncing_IsFalse_Initially()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsFalse(dataSource.IsSyncing);
    }

    [TestMethod]
    public virtual async Task OnSyncingStateChanged_Fires_DuringLoad()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var syncStarted = false;
        var syncEnded = false;

        dataSource.OnSyncingStateChanged += isSyncing =>
        {
            if (isSyncing) syncStarted = true;
            else syncEnded = true;
        };

        await dataSource.LoadDataSource();

        Assert.IsTrue(syncStarted);
        Assert.IsTrue(syncEnded);
    }

    #endregion

    #region IList Interface Tests

    [TestMethod]
    public virtual void IList_Add_ReturnsCorrectIndex()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Test" };
        var index = ((IList)dataSource).Add(item);

        Assert.AreEqual(0, index);
    }

    [TestMethod]
    public virtual void IList_Contains_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Test" };
        dataSource.Add(item);

        Assert.IsTrue(((IList)dataSource).Contains(item));
    }

    [TestMethod]
    public virtual void IList_IndexOf_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Test" };
        dataSource.Add(item);

        Assert.AreEqual(0, ((IList)dataSource).IndexOf(item));
    }

    [TestMethod]
    public virtual void IList_Insert_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "First" });
        var newItem = new TestOrderItem_All_True { Id = 2, ProductName = "Inserted" };

        ((IList)dataSource).Insert(0, newItem);

        Assert.AreEqual("Inserted", dataSource[0].ProductName);
    }

    [TestMethod]
    public virtual void IList_Remove_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Test" };
        dataSource.Add(item);

        ((IList)dataSource).Remove(item);
        Assert.AreEqual(0, dataSource.Count);
    }

    [TestMethod]
    public virtual void IList_Indexer_GetSet_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Original" });
        ((IList)dataSource)[0] = new TestOrderItem_All_True { Id = 1, ProductName = "Modified" };

        Assert.AreEqual("Modified", dataSource[0].ProductName);
    }

    [TestMethod]
    public virtual void ICollection_CopyTo_WorksCorrectly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Item1" });
        dataSource.Add(new TestOrderItem_All_True { Id = 2, ProductName = "Item2" });

        var array = new TestOrderItem_All_True[2];
        ((ICollection)dataSource).CopyTo(array, 0);

        Assert.AreEqual("Item1", array[0].ProductName);
        Assert.AreEqual("Item2", array[1].ProductName);
    }

    [TestMethod]
    public virtual void IsSynchronized_ReturnsTrue()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsTrue(dataSource.IsSynchronized);
    }

    [TestMethod]
    public virtual void SyncRoot_IsNotNull()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsNotNull(dataSource.SyncRoot);
    }

    [TestMethod]
    public virtual void IsFixedSize_ReturnsFalse()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsFalse(dataSource.IsFixedSize);
    }

    [TestMethod]
    public virtual void IsReadOnly_ReturnsFalse()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        Assert.IsFalse(((IList)dataSource).IsReadOnly);
    }

    #endregion

    #region Edge Cases

    [TestMethod]
    public virtual async Task Indexer_OutOfRange_ThrowsException()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        await dataSource.LoadDataSource();

        Assert.ThrowsExactly<ArgumentOutOfRangeException>(() => _ = dataSource[999]);
    }

    [TestMethod]
    public virtual void Add_ThenRemove_ClearsTracking()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Temporary" };

        dataSource.Add(item);
        Assert.AreEqual(1, dataSource.GetTrackingItems().Count);

        dataSource.Remove(item);
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);
    }

    [TestMethod]
    public virtual async Task ComplexWorkflow_AddUpdateRemoveSave()
    {
        var dataSource = CreateDataSource(_client, _crudTags);

        // Add items
        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Item1", Quantity = 10 });
        dataSource.Add(new TestOrderItem_All_True { Id = 2, ProductName = "Item2", Quantity = 20 });
        Assert.AreEqual(2, dataSource.GetTrackingItems().Count);

        // Save
        await dataSource.SaveChanges();
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);

        // Update
        dataSource[0] = new TestOrderItem_All_True { Id = 1, ProductName = "Updated1", Quantity = 100 };
        Assert.AreEqual(1, dataSource.GetTrackingItems().Count);
        Assert.AreEqual(TrackingState.Update, dataSource.GetTrackingItems()[0].TrackingState);

        // Remove
        dataSource.Remove(dataSource[1]);
        Assert.AreEqual(2, dataSource.GetTrackingItems().Count);

        // Save all changes
        await dataSource.SaveChanges();
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);
        Assert.AreEqual(1, dataSource.Count);
    }

    #endregion
}
