using AyCode.Core.Extensions;
using AyCode.Core.Helpers;
using AyCode.Core.Interfaces;
using AyCode.Core.Loggers;
using AyCode.Interfaces.Entities;
using AyCode.Services.SignalRs;
using AyCode.Utils.Extensions;
using AyCode.Utils.Wrappers;
using DevExpress.Blazor.Scheduler.Internal;
using FruitBank.Common;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.SignalRs;
using FruitBankHybrid.Shared.Services.SignalRs;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Orders;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Threading;

namespace FruitBankHybrid.Shared.Databases;

//public class ShippingTableItem : Shipping { }
//public class ShippingDocumentTableItem : ShippingDocument { }
public class ShippingItemTableItem : ShippingItem
{ }
//public class ShippingItemPalletTableItem : ShippingItemPallet { }
public class ProductDtoTableItem : ProductDto
{
    //[Newtonsoft.Json.JsonProperty]
    //public new ObservableCollection<GenericAttribute>? GenericAttributes { get; set; }
}

public class OrderDtoTableItem : OrderDto
{
    //[Newtonsoft.Json.JsonProperty]
    //public new ObservableCollection<GenericAttribute>? GenericAttributes { get; set; }
    //[Newtonsoft.Json.JsonProperty]
    //public new ObservableCollection<GenericAttribute>? OrderItemDtos { get; set; }
}

public class ShippingItemTable : SignalRDataSourceList<ShippingItemTableItem>
{
    private static readonly SignalRCrudTags SignalRCrudTags = new(SignalRTags.GetShippingItems, 0, SignalRTags.AddShippingItem, SignalRTags.UpdateShippingItem, 0);
    
    public ShippingItemTable(AcSignalRClientBase signalRClient, IAcLoggerBase? logger, params object[]? contextIds) : this(signalRClient, SignalRCrudTags, logger, contextIds)
    {

    }

    public ShippingItemTable(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, IAcLoggerBase? logger, params object[]? contextIds) : base(signalRClient, signalRCrudTags, logger, contextIds)
    {
    }
}

public class ProductDtoTable(FruitBankSignalRClient fruitBankSignalRClient) : AcObservableCollection<ProductDtoTableItem>
{
    //private readonly SemaphoreSlim _semaphoreSlim = new(1);
    public async Task<ProductDtoTable> LoadDataAsync(bool onlyIfEmpty = true)
    {
        if (onlyIfEmpty && Count > 0) return this;

        //using (await _semaphoreSlim.UseWaitAsync())
        using (await ObjectLock.GetSemaphore<ProductDto>().UseWaitAsync())
        {
            //Előfordulhat, h egy másik szálban már megtörtént a refresh... - J.
            if (onlyIfEmpty && Count > 0) return this;

            var items = (await fruitBankSignalRClient.GetProductDtoTableItems() ?? []);

            //Clear();
            this.Replace(items);
            //this.Replace(items.Cast<ProductDtoTableItem>());

            //foreach (var productDto in items)
            //{
            //    this.UpdateCollection(productDto, false);
            //}
        }

        return this;
    }
}
public class OrderDtoTable(FruitBankSignalRClient fruitBankSignalRClient) : AcObservableCollection<OrderDtoTableItem>
{
    //private readonly SemaphoreSlim _semaphoreSlim = new(1);

    public async Task<OrderDtoTable> LoadDataAsync(bool onlyIfEmpty = true)
    {
        if (onlyIfEmpty && Count > 0) return this;

        //using (await _semaphoreSlim.UseWaitAsync())
        using (await ObjectLock.GetSemaphore<ProductDto>().UseWaitAsync())
        {
            if (Count > 0) return this;

            var items = (await fruitBankSignalRClient.GetAllOrderDtoTableItems() ?? []);

            //Clear();
            this.Replace(items);
            //foreach (var orderDto in items)
            //{
            //    this.UpdateCollection(orderDto, false);
            //}
        }

        return this;
    }
}

public abstract class DatabaseTableBase<TDataItem> : SignalRDataSourceObservable<TDataItem> where TDataItem : class, IId<int>
{
    protected DatabaseTableBase(AcSignalRClientBase signalRClient, SignalRCrudTags signalRCrudTags, IAcLoggerBase? logger, params object[]? contextIds) : base(signalRClient, signalRCrudTags, logger, contextIds)
    {
    }

    //protected readonly SemaphoreSlim _semaphoreSlim = new(1);

    //public async Task<OrderDtoTable> LoadDataAsync(bool onlyIfEmpty = true)
    //{
    //    if (onlyIfEmpty && Count != 0) return this;

    //    using (await _semaphoreSlim.UseWaitAsync())
    //    {
    //        if (Count != 0) return this;

    //        var items = (await fruitBankSignalRClient.GetAllOrderDtoTableItems() ?? []);

    //        Clear();
    //        foreach (var orderDto in items)
    //        {
    //            this.Add(orderDto);
    //        }
    //    }

    //    return this;
    //}
}

public static class LoadingPanelVisibility
{
    public static bool Visible =false;
}

public static class ObjectLock
{
    private static readonly Dictionary<Type, SemaphoreSlim> SemaphoresByType = new();

    public static SemaphoreSlim GetSemaphore<TObject>()
    {
        lock (SemaphoresByType)
        {
            if (!SemaphoresByType.TryGetValue(typeof(TObject), out var semaphore))
            {
                semaphore = new SemaphoreSlim(1);
                SemaphoresByType[typeof(TObject)] = semaphore;
            }

            return semaphore;
        }
    }
}

public class DatabaseClient : DatabaseClientBase
{
    private readonly FruitBankSignalRClient _fruitBankSignalRClient;

    public DatabaseClient(FruitBankSignalRClient fruitBankSignalRClient)
    {
        _fruitBankSignalRClient = fruitBankSignalRClient;

        AddTable(new ProductDtoTable(_fruitBankSignalRClient));
        AddTable(new ShippingItemTable(_fruitBankSignalRClient, null));
        AddTable(new OrderDtoTable(_fruitBankSignalRClient));
    }

    public ProductDtoTable ProductDtoTable
    {
        get => (GetRows<ProductDtoTableItem>()! as ProductDtoTable)!;
        //set => AddRows(value);
    }

    public OrderDtoTable OrderDtoTable => (GetRows<OrderDtoTableItem>()! as OrderDtoTable)!;
}

public abstract class DatabaseClientBase
{
    //private ObservableCollection<IEntityInt> a = new ObservableCollection<IEntityInt>();
    private readonly ConcurrentDictionary<Type, object> _database = new();

    protected void AddTable<TEntity>(IList<TEntity> table)where TEntity: class, IEntityInt
    {
        if (GetTableObject<TEntity>() == null)
        {
            if (!_database.TryAdd(typeof(TEntity), table))
                return;
        }

        return;
    }

    protected IList<T>? GetTableObject<T>() where T : class, IEntityInt
    {
        return _database.GetValueOrDefault(typeof(T)) as IList<T>;
    }

    public IEnumerable<T>? GetRows<T>() where T : class, IEntityInt
    {
        return GetTableObject<T>();
    }

    public T? GetRow<T>(int id) where T : class, IEntityInt
    {
        return GetTableObject<T>()?.FirstOrDefault(x => x.Id == id) as T;
    }

    public T AddRow<T>(T entity) where T : class, IEntityInt
        => AddRow(GetTableObject<T>()!, entity);

    protected T AddRow<T>(in IList<T> table, T entity) where T : class, IEntityInt
    {
        table.Add(entity);
        return entity;
    }

    public void AddRows<T>(IEnumerable<T> entities) where T : class, IEntityInt
    {
        var table = GetTableObject<T>()!;

        foreach (var entity in entities)
        {
            AddRow(table, entity);
        }
    }

    public T? DeleteRow<T>(int id) where T : class, IEntityInt
    {
        var table = GetTableObject<T>();
        if (table == null) return null;

        var index = table.FindIndex(x => x.Id == id);
        if (index < 0) return null;

        var entity = table[index];
        table.RemoveAt(index);

        return entity as T;
    }
}