using AyCode.Core.Extensions;
using AyCode.Core.Helpers;
using AyCode.Core.Loggers;
using AyCode.Services.SignalRs;
using DevExpress.Utils.About;
using FruitBank.Common;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.Interfaces;
using FruitBank.Common.Models;
using FruitBank.Common.SignalRs;
using FruitBankHybrid.Shared.Databases;
using FruitBankHybrid.Shared.Services.Loggers;
using Mango.Nop.Core.Dtos;
using Mango.Nop.Core.Models;
using MessagePack.Resolvers;
using Microsoft.AspNetCore.SignalR.Client;
using Nop.Core.Domain.Customers;
using System.Collections.ObjectModel;
using System.ServiceModel.Channels;
using AyCode.Core.Serializers;
using Mango.Nop.Core.Entities;

namespace FruitBankHybrid.Shared.Services.SignalRs
{
    public class FruitBankSignalRClient : AcSignalRClientBase, IFruitBankDataControllerClient, ICustomOrderSignalREndpointClient, IStockSignalREndpointClient
    {
        public FruitBankSignalRClient(IHubConnectionBuilder hubBuilder, Func<string, LoggerClient> loggerFactory)
            : base(hubBuilder, loggerFactory(nameof(FruitBankSignalRClient)))
        {
            EnableBinaryDiagnostics = FruitBankConstClient.SignalRSerializerDiagnosticLog;
            ConstHelper.NameByValue<SignalRTags>(0);
        }

        /// <summary>
        /// MessageTag, JSON
        /// </summary>
        public event Func<int, SignalResponseDataMessage?, Task> OnMessageReceived = null!;

        protected override async Task MessageReceived(int messageTag, SignalParams signalParams, object data)
        {
            var responseDataMessage = new SignalResponseDataMessage
            {
                Status = signalParams.Status,
                DataSerializerType = AcSerializerType.Binary,
                RawResponseData = data
            };
            await OnMessageReceived(messageTag, responseDataMessage);
        }

        private void SendMessageToAllClients(int messageTag, string message)
        {
            if (messageTag == SignalRTags.NotificationReceived)
            {
                Logger.Info(message);
            }
        }

        public Task<List<MeasuringModel>?> GetMeasuringModels() => GetAllAsync<List<MeasuringModel>>(SignalRTags.GetMeasuringModels);

        #region Partner
        public Task<List<Partner>?> GetPartners() => GetAllAsync<List<Partner>>(SignalRTags.GetPartners);
        public Task<Partner?> GetPartnerById(int id) => GetByIdAsync<Partner?>(SignalRTags.GetPartnerById, id);
        public Task<Partner?> AddPartner(Partner partner) => PostDataAsync(SignalRTags.AddPartner, partner);
        public Task<Partner?> UpdatePartner(Partner partner) => PostDataAsync(SignalRTags.UpdatePartner, partner);
        #endregion Partner

        #region PartnerDepot
        public Task<List<PartnerDepot>?> GetPartnerDepots() => GetAllAsync<List<PartnerDepot>>(SignalRTags.GetPartnerDepots);
        public Task<PartnerDepot?> GetPartnerDepotById(int id) => GetByIdAsync<PartnerDepot?>(SignalRTags.GetPartnerDepotById, id);
        public Task<List<PartnerDepot>?> GetPartnerDepotsByPartnerId(int partnerId) => GetAllAsync<List<PartnerDepot>>(SignalRTags.GetPartnerDepotsByPartnerId, [partnerId]);
        public Task<PartnerDepot?> AddPartnerDepot(PartnerDepot partnerDepot) => PostDataAsync(SignalRTags.AddPartnerDepot, partnerDepot);
        public Task<PartnerDepot?> UpdatePartnerDepot(PartnerDepot partnerDepot) => PostDataAsync(SignalRTags.UpdatePartnerDepot, partnerDepot);
        #endregion PartnerDepot

        #region EkaerHistory
        public Task<List<EkaerHistory>?> GetEkaerHistories(EkaerHistoryFilter filter) => GetAllAsync<List<EkaerHistory>>(SignalRTags.GetEkaerHistories, [filter]);
        public Task<EkaerHistory?> GetEkaerHistoryById(int id) => GetByIdAsync<EkaerHistory?>(SignalRTags.GetEkaerHistoryById, id);
        public Task<List<EkaerHistory>?> GetEkaerHistoriesByForeignKey(int foreignKey) => GetAllAsync<List<EkaerHistory>>(SignalRTags.GetEkaerHistoriesByForeignKey, [foreignKey]);
        public Task<EkaerHistory?> AddEkaerHistory(EkaerHistory ekaerHistory) => PostDataAsync(SignalRTags.AddEkaerHistory, ekaerHistory);
        public Task<EkaerHistory?> UpdateEkaerHistory(EkaerHistory ekaerHistory) => PostDataAsync(SignalRTags.UpdateEkaerHistory, ekaerHistory);
        public Task<EkaerHistory?> GenerateEkaerXmlDocument(int ekaerHistoryId) => GetByIdAsync<EkaerHistory?>(SignalRTags.GenerateEkaerXmlDocument, ekaerHistoryId);
        public Task<EkaerHistory?> CreateEkaerHistory(int foreignKey, bool isOutgoing) => GetByIdAsync<EkaerHistory?>(SignalRTags.CreateEkaerHistory, [foreignKey, isOutgoing]);
        public Task<EkaerCreateResult?> CreateMissingEkaerHistories(DateTime fromDate) => GetByIdAsync<EkaerCreateResult?>(SignalRTags.CreateMissingEkaerHistories, fromDate);
        public Task<int> GetEkaerHistoryCount(EkaerHistoryFilter filter) => GetByIdAsync<int>(SignalRTags.GetEkaerHistoryCount, filter);
        #endregion EkaerHistory

        #region CargoPartner
        public Task<List<CargoPartner>?> GetCargoPartners() => GetAllAsync<List<CargoPartner>>(SignalRTags.GetCargoPartners);
        public Task<CargoPartner?> GetCargoPartnerById(int id) => GetByIdAsync<CargoPartner?>(SignalRTags.GetCargoPartnerById, id);
        public Task<CargoPartner?> AddCargoPartner(CargoPartner cargoPartner) => PostDataAsync(SignalRTags.AddCargoPartner, cargoPartner);
        public Task<CargoPartner?> UpdateCargoPartner(CargoPartner cargoPartner) => PostDataAsync(SignalRTags.UpdateCargoPartner, cargoPartner);
        #endregion CargoPartner

        #region CargoTruck
        public Task<List<CargoTruck>?> GetCargoTrucks() => GetAllAsync<List<CargoTruck>>(SignalRTags.GetCargoTrucks);
        public Task<CargoTruck?> GetCargoTruckById(int id) => GetByIdAsync<CargoTruck?>(SignalRTags.GetCargoTruckById, id);
        public Task<List<CargoTruck>?> GetCargoTrucksByCargoPartnerId(int cargoPartnerId) => GetAllAsync<List<CargoTruck>>(SignalRTags.GetCargoTrucksByCargoPartnerId, [cargoPartnerId]);
        public Task<CargoTruck?> AddCargoTruck(CargoTruck cargoTruck) => PostDataAsync(SignalRTags.AddCargoTruck, cargoTruck);
        public Task<CargoTruck?> UpdateCargoTruck(CargoTruck cargoTruck) => PostDataAsync(SignalRTags.UpdateCargoTruck, cargoTruck);
        #endregion CargoTruck


        #region Shipping

        public Task<List<Shipping>?> GetShippings()
            => GetAllAsync<List<Shipping>>(SignalRTags.GetShippings);

        public Task<List<Shipping>?> GetNotMeasuredShippings()
            => GetAllAsync<List<Shipping>>(SignalRTags.GetNotMeasuredShippings);

        public Task<Shipping?> GetShippingById(int id)
            => GetByIdAsync<Shipping?>(SignalRTags.GetShippingById, id);

        public Task<Shipping?> AddShipping(Shipping shipping)
            => PostDataAsync(SignalRTags.AddShipping, shipping);

        public Task<Shipping?> UpdateShipping(Shipping shipping)
            => PostDataAsync(SignalRTags.UpdateShipping, shipping);

        public Task<List<Partner>?> ProcessAndSaveFullShippingJson(string fullShippingJson, int customerId)
            => GetAllAsync<List<Partner>>(SignalRTags.ProcessAndSaveFullShippingJson, [fullShippingJson, customerId]);

        #endregion Shipping

        #region ShippingItem

        public Task<List<ShippingItem>?> GetShippingItems()
            => GetAllAsync<List<ShippingItem>>(SignalRTags.GetShippingItems);

        public Task<List<ShippingItem>?> GetShippingItemsByDocumentId(int shippingDocumentId)
            => GetAllAsync<List<ShippingItem>>(SignalRTags.GetShippingItemsByDocumentId, [shippingDocumentId]);

        public Task<List<ShippingItem>?> GetShippingItemsByShippingId(int shippingId)
            => GetAllAsync<List<ShippingItem>>(SignalRTags.GetShippingItemsByShippingId, [shippingId]);

        public Task<List<ShippingItem>?> GetShippingItemsByPartnerId(int partnerId)
            => GetAllAsync<List<ShippingItem>>(SignalRTags.GetShippingItemsByPartnerId, [partnerId]);

        public Task<ShippingItem?> GetShippingItemById(int id)
            => GetByIdAsync<ShippingItem?>(SignalRTags.GetShippingItemById, id);

        public Task<ShippingItem?> AddShippingItem(ShippingItem shippingItem)
            => PostDataAsync(SignalRTags.AddShippingItem, shippingItem);

        public Task<ShippingItem?> UpdateShippingItem(ShippingItem shippingItem)
            => PostDataAsync(SignalRTags.UpdateShippingItem, shippingItem);

        public Task<ShippingItem?> UpdateMeasuredShippingItem(ShippingItem shippingItem)
            => PostDataAsync(SignalRTags.UpdateMeasuredShippingItem, shippingItem);

        #endregion ShippingItem

        #region ShippingItemPallet

        public Task<List<ShippingItemPallet>?> GetShippingItemPallets()
            => GetAllAsync<List<ShippingItemPallet>>(SignalRTags.GetShippingItemPallets);

        public Task<ShippingItemPallet?> AddShippingItemPallet(ShippingItemPallet shippingItemPallet)
            => PostDataAsync(SignalRTags.AddShippingItemPallet, shippingItemPallet);

        public Task<ShippingItemPallet?> UpdateShippingItemPallet(ShippingItemPallet shippingItemPallet)
            => PostDataAsync(SignalRTags.UpdateShippingItemPallet, shippingItemPallet);

        public Task<ShippingItemPallet?> AddOrUpdateMeasuredShippingItemPallet(ShippingItemPallet shippingItemPallet)
            => PostDataAsync(SignalRTags.AddOrUpdateMeasuredShippingItemPallet, shippingItemPallet);

        public Task<ShippingItem?> AddOrUpdateMeasuredShippingItemPallets(List<ShippingItemPallet> shippingItemPallets)
            => PostDataAsync<List<ShippingItemPallet>, ShippingItem>(SignalRTags.AddOrUpdateMeasuredShippingItemPallets, shippingItemPallets);

        #endregion ShippingItemPallet

        #region ShippingDocument

        public Task<List<ShippingDocument>?> GetShippingDocuments()
            => GetAllAsync<List<ShippingDocument>>(SignalRTags.GetShippingDocuments);

        public Task<ShippingDocument?> GetShippingDocumentById(int id)
            => GetByIdAsync<ShippingDocument?>(SignalRTags.GetShippingDocumentById, id);

        public Task<List<ShippingDocument>?> GetShippingDocumentsByShippingId(int shippingId)
            => GetAllAsync<List<ShippingDocument>>(SignalRTags.GetShippingDocumentsByShippingId, [shippingId]);

        public Task<List<ShippingDocument>?> GetShippingDocumentsByProductId(int productId)
            => GetAllAsync<List<ShippingDocument>>(SignalRTags.GetShippingDocumentsByProductId, [productId]);

        public Task<List<ShippingDocument>?> GetShippingDocumentsByPartnerId(int partnerId)
            => GetAllAsync<List<ShippingDocument>>(SignalRTags.GetShippingDocumentsByPartnerId, [partnerId]);

        public Task<ShippingDocument?> AddShippingDocument(ShippingDocument shippingDocument)
            => PostDataAsync(SignalRTags.AddShippingDocument, shippingDocument);

        public Task<ShippingDocument?> UpdateShippingDocument(ShippingDocument shippingDocument)
            => PostDataAsync(SignalRTags.UpdateShippingDocument, shippingDocument);
        #endregion ShippingDocument

        #region Customer

        public Task<List<CustomerDto>?> GetMeasuringUsers()
            => GetAllAsync<List<CustomerDto>>(SignalRTags.GetMeasuringUsers);

        public Task<List<CustomerRole>?> GetCustomerRolesByCustomerId(int customerId)
            => GetAllAsync<List<CustomerRole>>(SignalRTags.GetCustomerRolesByCustomerId, [customerId]);
        #endregion Customer

        #region Product
        //TODO: itt ProductDtoTableItem kéne, csak az AycodeHubProtocol ProductDto-ra deserialize-ol! - J.
        //public Task<List<ProductDto>?> GetProductDtoTableItems() => GetAllAsync<List<ProductDto>>(SignalRTags.GetProductDtos);
        // IDEIGLENES gyors fix: a GetProductDtos tagon a szerver List<ProductDto>-t küld, a binár
        // protokoll viszont a wire-típusba (ProductDto) deszerializál — így a közvetlen
        // List<ProductDtoTableItem> kérés üres lett (a ProductDtoTableItem a ProductDto kliens-oldali
        // leszármazottja, amit a szerver nem ismer). A működő GetProductDtos()-t hívjuk, és bináris
        // round-trippel a kért leszármazottra alakítjuk (azonos mezők). VÉGLEGES megoldás: requestId →
        // kért-típus a protokollban (AQN-leváltás) — lásd AyCode.Services/docs/SIGNALR_BINARY_PROTOCOL.
        public async Task<List<ProductDtoTableItem>?> GetProductDtoTableItems()
        {
            var dtos = await GetProductDtos();
            if (dtos == null) return null;

            var bytes = SignalRSerializationHelper.SerializeToBinary(dtos);
            return SignalRSerializationHelper.DeserializeFromBinary<List<ProductDtoTableItem>>(bytes);
        }

        public Task<List<ProductDto>?> GetProductDtos() => GetAllAsync<List<ProductDto>>(SignalRTags.GetProductDtos);

        //public Task<List<MeasuringProductDto>?> GetAllMeasuringProductDtos()
        //    => GetAllAsync<List<MeasuringProductDto>>(SignalRTags.GetAllMeasuringProductDtos);

        public Task<ProductDto?> GetProductDtoById(int productId) => GetByIdAsync<ProductDto?>(SignalRTags.GetMeasuringProductDtoById, productId);

        #endregion Product

        #region Authenticate
        public Task<MgLoginModelResponse?> LoginMeasuringUser(string customerEmail, string customerPassword)
            => LoginMeasuringUser(new MgLoginModelRequest(customerEmail, customerPassword));

        public Task<MgLoginModelResponse?> LoginMeasuringUser(MgLoginModelRequest loginModelRequest)
            => PostDataAsync<MgLoginModelRequest, MgLoginModelResponse>(SignalRTags.AuthenticateUser, loginModelRequest);
        #endregion Authenticate

        #region GridLayout
        public Task<string?> GetGridLayout(string key, int userId)
            => GetByIdAsync<string>(SignalRTags.GetGridLayoutTag, [key, userId]);

        public Task<bool> SaveGridLayout(string key, string layoutJson, int userId)
            => PostAsync<bool>(SignalRTags.SaveGridLayoutTag, [key, layoutJson, userId]);

        public Task<bool> DeleteGridLayout(string key, int userId)
            => PostAsync<bool>(SignalRTags.DeleteGridLayoutTag, [key, userId]);
        #endregion GridLayout

        #region Orders
        public Task<List<OrderDtoTableItem>?> GetAllOrderDtoTableItems()
            => GetAllAsync<List<OrderDtoTableItem>>(SignalRTags.GetAllOrderDtos);

        public Task<List<OrderDto>?> GetAllOrderDtos()
            => GetAllAsync<List<OrderDto>>(SignalRTags.GetAllOrderDtos);

        public Task<OrderDto?> GetOrderDtoById(int orderId)
            => GetByIdAsync<OrderDto?>(SignalRTags.GetOrderDtoById, orderId);

        public Task<List<OrderDto>?> GetPendingOrderDtos()
            => GetAllAsync<List<OrderDto>>(SignalRTags.GetPendingOrderDtos);

        public Task<List<OrderDto>?> GetPendingOrderDtosForMeasuring(int lastDaysCount)
            => GetAllAsync<List<OrderDto>>(SignalRTags.GetPendingOrderDtosForMeasuring, [lastDaysCount]);

        public Task<List<OrderDto>?> GetAllOrderDtoByIds(int[] orderIds)
            => GetAllAsync<List<OrderDto>>(SignalRTags.GetAllOrderDtoByIds, [orderIds]);

        public Task<List<OrderItemDto>?> GetAllOrderItemDtos()
            => GetAllAsync<List<OrderItemDto>>(SignalRTags.GetAllOrderItemDtos);

        public Task<OrderItemPallet?> AddOrUpdateMeasuredOrderItemPallet(OrderItemPallet orderItemPallet)
            => PostDataAsync(SignalRTags.AddOrUpdateMeasuredOrderItemPallet, orderItemPallet);

        public Task<OrderDto?> StartMeasuring(int orderId, int userId)
            => GetByIdAsync<OrderDto>(SignalRTags.StartMeasuring, [orderId, userId]);

        public Task<OrderDto?> SetOrderStatusToComplete(int orderId, int revisorId)
            => GetByIdAsync<OrderDto>(SignalRTags.SetOrderStatusToComplete, [orderId, revisorId]);

        public Task<List<OrderDto>?> GetAllOrderDtoByProductId(int productId)
            => GetAllAsync<List<OrderDto>>(SignalRTags.GetAllOrderDtoByProductId, [productId]);

        public Task<OrderItemDto?> GetOrderItemDtoById(int orderItemId)
            => GetByIdAsync<OrderItemDto>(SignalRTags.GetOrderItemDtoById, [orderItemId]);

        public Task<List<OrderItemDto>?> GetAllOrderItemDtoByOrderId(int orderId)
            => GetAllAsync<List<OrderItemDto>>(SignalRTags.GetAllOrderItemDtoByOrderId, [orderId]);

        public Task<List<OrderItemDto>?> GetAllOrderItemDtoByProductId(int productId)
            => GetAllAsync<List<OrderItemDto>>(SignalRTags.GetAllOrderItemDtoByProductId, [productId]);

        public Task<List<OrderItemPallet>?> GetAllOrderItemPallets()
            => GetAllAsync<List<OrderItemPallet>>(SignalRTags.GetAllOrderItemPallets);

        public Task<OrderItemPallet?> GetOrderItemPalletById(int orderItemPalletId)
            => GetByIdAsync<OrderItemPallet>(SignalRTags.GetOrderItemPalletById, [orderItemPalletId]);

        public Task<List<OrderItemPallet>?> GetAllOrderItemPalletByOrderItemId(int orderItemId)
            => GetAllAsync<List<OrderItemPallet>>(SignalRTags.GetAllOrderItemPalletByOrderItemId, [orderItemId]);

        public Task<List<OrderItemPallet>?> GetAllOrderItemPalletByOrderId(int orderId)
            => GetAllAsync<List<OrderItemPallet>>(SignalRTags.GetAllOrderItemPalletByOrderId, [orderId]);

        public Task<List<OrderItemPallet>?> GetAllOrderItemPalletByProductId(int productId)
            => GetAllAsync<List<OrderItemPallet>>(SignalRTags.GetAllOrderItemPalletByProductId, [productId]);

        public Task<List<StockQuantityHistoryDto>?> GetStockQuantityHistoryDtos()
            => GetAllAsync<List<StockQuantityHistoryDto>>(SignalRTags.GetStockQuantityHistoryDtos);

        public Task<List<StockQuantityHistoryDto>?> GetStockQuantityHistoryDtosByProductId(int productId)
            => GetAllAsync<List<StockQuantityHistoryDto>>(SignalRTags.GetStockQuantityHistoryDtosByProductId, [productId]);

        #endregion Orders

        public async Task<List<GenericAttributeDto>?> GetGenericAttributeDtosByEntityIdAndKeyGroup(int productId, string keyGroup, int storeId)
        {
            throw new NotImplementedException();
        }

        public async Task<GenericAttributeDto?> AddGenericAttributeDto(GenericAttributeDto genericAttributeDto)
        {
            throw new NotImplementedException();
        }

        public async Task<GenericAttributeDto?> UpdateGenericAttributeDto(GenericAttributeDto genericAttributeDto)
        {
            throw new NotImplementedException();
        }


        public Task<List<StockTaking>?> GetStockTakings(bool loadRelations) => GetAllAsync<List<StockTaking>>(SignalRTags.GetStockTakings, [loadRelations]);

        public async Task<List<StockTaking>?> GetStockTakingsByProductId(int productId)
        {
            throw new NotImplementedException();
        }

        public Task<StockTaking?> AddStockTaking(StockTaking stockTaking) => PostDataAsync(SignalRTags.AddStockTaking, stockTaking);
        public Task<StockTakingItem?> RefreshStockTakingItem(int stockTakingItemId) => GetByIdAsync<StockTakingItem>(SignalRTags.RefreshStockTakingItem, stockTakingItemId);

        public Task<StockTaking?> UpdateStockTaking(StockTaking stockTaking) => PostDataAsync(SignalRTags.UpdateStockTaking, stockTaking);

        public Task<List<StockTakingItem>?> GetStockTakingItems() => GetAllAsync<List<StockTakingItem>>(SignalRTags.GetStockTakingItems);

        public Task<StockTakingItem?> GetStockTakingItemsById(int stockTakingItemId)
            => GetByIdAsync<StockTakingItem>(SignalRTags.GetStockTakingItemsById, [stockTakingItemId]);

        public Task<List<StockTakingItem>?> GetStockTakingItemsByProductId(int productId)
            => GetAllAsync<List<StockTakingItem>>(SignalRTags.GetStockTakingItemsByProductId, [productId]);

        public Task<List<StockTakingItem>?> GetStockTakingItemsByStockTakingId(int stockTakingId)
            => GetAllAsync<List<StockTakingItem>>(SignalRTags.GetStockTakingItemsByStockTakingId, [stockTakingId]);

        public async Task<StockTakingItem?> AddStockTakingItem(StockTakingItem stockTakingItem)
        {
            throw new NotImplementedException();
        }

        public async Task<StockTakingItem?> UpdateStockTakingItem(StockTakingItem stockTakingItem)
        {
            throw new NotImplementedException();
        }

        public async Task<List<StockTakingItemPallet>?> GetStockTakingItemPallets()
        {
            throw new NotImplementedException();
        }

        public async Task<List<StockTakingItemPallet>?> GetStockTakingItemPalletsByProductId(int productId)
        {
            throw new NotImplementedException();
        }

        public Task<StockTaking?> CloseStockTaking(int stockTakingId)
            => PostDataAsync<int, StockTaking?>(SignalRTags.CloseStockTaking, stockTakingId);
        

        public async Task<StockTakingItemPallet?> AddStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet)
        {
            throw new NotImplementedException();
        }

        public async Task<StockTakingItemPallet?> UpdateStockTakingItemPallet(StockTakingItemPallet stockTakingItemPallet)
        {
            throw new NotImplementedException();
        }
    }
}
