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

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

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

    [TestMethod]
    public virtual async Task Add_WithAutoSave_AddsItem()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var newItem = new TestOrderItem_All_True { Id = 100, ProductName = "New Product", Quantity = 5, UnitPrice = 50m };
        var result = await dataSource.Add(newItem, autoSave: true);

        Assert.AreEqual(1, dataSource.Count);
        Assert.AreEqual("New Product", result.ProductName);
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);
    }

    [TestMethod]
    public virtual void Add_WithoutAutoSave_AddsToTrackingOnly()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var newItem = new TestOrderItem_All_True { Id = 100, ProductName = "New Product" };
        dataSource.Add(newItem);

        Assert.AreEqual(1, dataSource.Count);
        Assert.AreEqual(1, dataSource.GetTrackingItems().Count);
        Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState);
    }

    [TestMethod]
    public virtual void Add_DuplicateId_ThrowsException()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        dataSource.Add(new TestOrderItem_All_True { Id = 100, ProductName = "First" });

        Assert.ThrowsExactly<ArgumentException>(() =>
        {
            dataSource.Add(new TestOrderItem_All_True { Id = 100, ProductName = "Duplicate" });
        });
    }

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

        Assert.ThrowsExactly<ArgumentNullException>(() =>
        {
            dataSource.Add(new TestOrderItem_All_True { Id = 0, ProductName = "Invalid" });
        });
    }

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

        var items = new[]
        {
            new TestOrderItem_All_True { Id = 101, ProductName = "Item 1" },
            new TestOrderItem_All_True { Id = 102, ProductName = "Item 2" },
            new TestOrderItem_All_True { Id = 103, ProductName = "Item 3" }
        };
        dataSource.AddRange(items);
        Assert.AreEqual(3, dataSource.Count);
    }

    #endregion

    #region AddOrUpdate Tests

    [TestMethod]
    public virtual async Task AddOrUpdate_AddsNew_WhenNotExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var newItem = new TestOrderItem_All_True { Id = 200, ProductName = "Brand New" };
        var result = await dataSource.AddOrUpdate(newItem, autoSave: true);

        Assert.AreEqual(1, dataSource.Count);
        Assert.AreEqual("Brand New", result.ProductName);
    }

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

        var existingId = dataSource[0].Id;
        var updatedItem = new TestOrderItem_All_True { Id = existingId, ProductName = "Updated Name", Quantity = 999 };
        _ = await dataSource.AddOrUpdate(updatedItem, autoSave: true);

        Assert.AreEqual(3, dataSource.Count);
        Assert.AreEqual("Updated Name", dataSource[0].ProductName);
    }

    #endregion

    #region Insert Tests

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

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "First" });
        dataSource.Add(new TestOrderItem_All_True { Id = 3, ProductName = "Third" });
        dataSource.Insert(1, new TestOrderItem_All_True { Id = 2, ProductName = "Second" });

        Assert.AreEqual(3, dataSource.Count);
        Assert.AreEqual("Second", dataSource[1].ProductName);
        Assert.AreEqual(3, dataSource.GetTrackingItems().Count);
    }

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

        var newItem = new TestOrderItem_All_True { Id = 500, ProductName = "Inserted" };
        _ = await dataSource.Insert(0, newItem, autoSave: true);

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

    #endregion

    #region Update Tests

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

        var updatedItem = new TestOrderItem_All_True
        {
            Id = dataSource[0].Id,
            ProductName = "Updated Product",
            Quantity = 100
        };

        _ = await dataSource.Update(0, updatedItem, autoSave: true);

        Assert.AreEqual("Updated Product", dataSource[0].ProductName);
        Assert.AreEqual(100, dataSource[0].Quantity);
    }

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

        var updatedItem = new TestOrderItem_All_True
        {
            Id = dataSource[1].Id,
            ProductName = "Updated B",
            Quantity = 50
        };

        _ = await dataSource.Update(updatedItem, autoSave: true);

        Assert.AreEqual("Updated B", dataSource[1].ProductName);
    }

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

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

        Assert.AreEqual(1, dataSource.GetTrackingItems().Count);
        Assert.AreEqual(TrackingState.Add, dataSource.GetTrackingItems()[0].TrackingState);
    }

    #endregion

    #region Remove Tests

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

        var idToRemove = dataSource[0].Id;
        var result = await dataSource.Remove(idToRemove, autoSave: true);

        Assert.IsTrue(result);
        Assert.AreEqual(2, dataSource.Count);
    }

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

        var itemToRemove = dataSource[1];
        var result = await dataSource.Remove(itemToRemove, autoSave: true);

        Assert.IsTrue(result);
        Assert.AreEqual(2, dataSource.Count);
    }

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

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

        dataSource.GetTrackingItems().Clear();
        dataSource.Remove(dataSource[0]);

        Assert.AreEqual(0, dataSource.Count);
        Assert.AreEqual(0, dataSource.GetTrackingItems().Count);
        Assert.AreEqual(TrackingState.Remove, dataSource.GetTrackingItems()[0].TrackingState);
    }

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

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "First" });
        dataSource.Add(new TestOrderItem_All_True { Id = 2, ProductName = "Second" });
        dataSource.Add(new TestOrderItem_All_True { Id = 3, ProductName = "Third" });

        dataSource.GetTrackingItems().Clear();
        dataSource.RemoveAt(1);

        Assert.AreEqual(2, dataSource.Count);
        Assert.AreEqual("First", dataSource[0].ProductName);
        Assert.AreEqual("Third", dataSource[1].ProductName);
    }

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

        await dataSource.LoadDataSource();
        await dataSource.RemoveAt(0, autoSave: true);

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

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

        dataSource.Add(new TestOrderItem_All_True { Id = 1, ProductName = "Test" });
        var result = dataSource.TryRemove(1, out var removedItem);

        Assert.IsTrue(result);
        Assert.IsNotNull(removedItem);
        Assert.AreEqual("Test", removedItem.ProductName);
    }

    [TestMethod]
    public virtual void TryRemove_ReturnsFalse_WhenNotExists()
    {
        var dataSource = CreateDataSource(_client, _crudTags);
        var result = dataSource.TryRemove(9999, out var removedItem);

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

    #endregion
}
