//using AyCode.Core.Loggers;

using AyCode.Core.Loggers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Services.Server.SignalRs;
using AyCode.Services.SignalRs;
using DevExpress.AspNetCore;
using FruitBank.Common;
using FruitBank.Common.Interfaces;
using FruitBank.Common.Server.Interfaces;
using FruitBank.Common.Server.Services.Loggers;
using FruitBank.Common.Server.Services.SignalRs;
using Mango.Nop.Services;
using Mango.Nop.Services.Loggers;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Nop.Core;
using Nop.Core.Domain.Orders;
using Nop.Core.Infrastructure;
using Nop.Data;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers;
using Nop.Plugin.Misc.FruitBankPlugin.Controllers;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Factories;
using Nop.Plugin.Misc.FruitBankPlugin.Filters;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Events;
using Nop.Services.Messages;
using Nop.Web.Areas.Admin.Factories;
using Nop.Web.Areas.Admin.Models.Catalog;
using Nop.Web.Areas.Admin.Models.Orders;
using System.Net.Http.Headers;
using Mango.Nop.Core.Loggers;
using AyCode.Services.Nav;
using AyCode.Services.Nav.Ekaer;
using AyCode.Services.Nav.Ekaer.Models;
using FruitBank.Common.Services.Ekaer;
using FruitBank.Common.Server.Services.Ekaer;
using System.Security.Authentication;

namespace Nop.Plugin.Misc.FruitBankPlugin.Infrastructure;

public class PluginNopStartup : INopStartup
{
    /// <summary>
    /// Add and configure any of the middleware
    /// </summary>
    /// <param name="services">Collection of service descriptors</param>
    /// <param name="configuration">Configuration of the application</param>
    public void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        // Enforce registration of source generated serializers which might not run due to AssemblyLoadContext restrictions
        Mango.Nop.Core.AcBinaryForcedInit.ForceRegister();

        services.AddScoped<IAcLogWriterBase, ConsoleLogWriter>();

        services.AddTransient<INopLoggerMsSqlNopDataProvider, NopLoggerMsSqlNopDataProvider>();
        services.AddScoped<IAcLogWriterBase, NopLogWriter>();

        services.AddSingleton<LoggerToLoggerApiController>();
        //services.AddSingleton<SessionService>();

        services.Configure<RazorViewEngineOptions>(options =>
        {
            options.ViewLocationExpanders.Add(new ViewLocationExpander());
        });

        services.AddSingleton<ILockService, LockService>();
        services.AddScoped<FruitBankAttributeService>();

        services.AddScoped<ProductDtoDbTable>();
        services.AddScoped<OrderDtoDbTable>();
        services.AddScoped<OrderItemDtoDbTable>();
        services.AddScoped<OrderItemPalletDbTable>();

        services.AddScoped<PartnerDbTable>();
        services.AddScoped<PartnerDepotDbTable>();
        services.AddScoped<EkaerHistoryDbTable>();
        services.AddScoped<EkaerHistoryMappingDbTable>();
        services.AddScoped<CargoPartnerDbTable>();
        services.AddScoped<CargoTruckDbTable>();

        services.AddScoped<ShippingDbTable>();
        services.AddScoped<ShippingDocumentDbTable>();
        services.AddScoped<ShippingItemDbTable>();
        services.AddScoped<ShippingItemPalletDbTable>();
        services.AddScoped<FilesDbTable>();
        services.AddScoped<ShippingDocumentToFilesDbTable>();

        services.AddScoped<StockQuantityHistoryDtoDbTable>();

        services.AddScoped<StockTakingDbTable>();
        services.AddScoped<StockTakingItemDbTable>();
        services.AddScoped<StockTakingItemPalletDbTable>();
        services.AddScoped<CustomerCreditDbTable>();
        services.AddScoped<PreOrderDbTable>();
        services.AddScoped<PreOrderItemDbTable>();
        services.AddScoped<PreOrderDbContext>();

        services.AddScoped<StockTakingDbContext>();
        services.AddScoped<FruitBankDbContext>();

        services.AddScoped<SignalRSendToClientService>();
        services.AddScoped<IFruitBankDataControllerServer, FruitBankDataController>();
        services.AddScoped<ICustomOrderSignalREndpointServer, CustomOrderSignalREndpoint>();
        services.AddScoped<IStockSignalREndpointServer, StockSignalREndpointServer>();

        //services.AddScoped<CustomModelFactory, ICustomerModelFactory>();
        //services.AddScoped<IPriceCalculationService, CustomPriceCalculationService>();
        //services.AddScoped<PriceCalculationService, CustomPriceCalculationService>();
        services.Replace(
        ServiceDescriptor.Scoped<IPriceCalculationService, CustomPriceCalculationService>()
    );
        //services.AddScoped<IMessageTokenProvider, FruitBankMessageTokenProvider>();
        services.Replace(
        ServiceDescriptor.Scoped<IMessageTokenProvider, FruitBankMessageTokenProvider>()
        );
        services.AddScoped<IConsumer<OrderPlacedEvent>, EventConsumer>();
        services.AddScoped<IOrderMeasurementService, OrderMeasurementService>();
        services.AddScoped<PendingMeasurementCheckoutFilter>();
        services.AddScoped<InnVoiceApiService>();
        services.AddScoped<InnVoiceOrderService>();

        services.AddScoped<MeasurementService>();
        services.AddScoped<ReplicateService>();

        services.AddHttpClient<ReplicateService>(client =>
        {
            client.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Bearer", "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh");
        });

        //services.AddScoped<OrderListModel, OrderListModelExtended>();
        //services.AddScoped<OrderModel, OrderModelExtended>();
        //services.AddScoped<OrderSearchModel, OrderSearchModelExtended>();

        services.AddScoped<IOrderModelFactory, CustomOrderModelFactory>();
        services.AddScoped<IProductModelFactory, CustomProductModelFactory>();
        services.AddScoped<IGenericAttributeService, GenericAttributeService>();
        services.AddScoped<CerebrasAPIService>();
        services.AddScoped<OpenAIApiService>();
        //services.AddScoped<IAIAPIService, OpenAIApiService>();
        services.AddScoped<AICalculationService>();

        // Z.ai GLM-OCR — nagy PDF-eknél 3 perces timeout szükséges (1.86 oldal/mp sebesség)
        services.AddHttpClient<ZaiService>(client =>
        {
            client.Timeout = TimeSpan.FromMinutes(3);
        });
        services.AddScoped<PdfToImageService>();
        services.AddScoped<FruitBankNotificationService>();
        services.AddScoped<FruitBankOrderItemService>();
        services.AddScoped<PreOrderConversionService>();
        services.AddSingleton<IFileStorageProvider>(sp =>
            new LocalFileStorageProvider() // Uses default wwwroot/uploads
                                           // Or specify custom path:
                                           // new LocalFileStorageProvider(Path.Combine(Directory.GetCurrentDirectory(), "MyCustomPath"))
        );

        // Register the file storage service
        services.AddScoped<FileStorageService>();
        services.AddScoped<ICustomerCreditService, CustomerCreditService>();

        // ── EKÁER (NAV közúti áruforgalom-bejelentés): map → validate → send ──────────
        services.AddScoped<IShippingToEkaerMapper, ShippingToEkaerMapper>();          // FruitBank.Common — leképezés
        services.AddScoped<IEkaerTradeCardValidator, EkaerTradeCardValidator>();      // AyCode.Services — NAV-validáció
        services.AddScoped<IEkaerSubmitService, EkaerSubmitService>();                // AyCode.Services — validate→send
        services.AddScoped<IFruitBankEkaerService, FruitBankEkaerService>();          // FruitBank.Common.Server — fogyasztó

        services.AddHttpClient<EkaerManageService>()                                  // AyCode.Services — NAV HTTP
            .ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler { SslProtocols = SslProtocols.Tls12 }); // a NAV TLS 1.2-t vár

        // A NAV-fiók hitelesítő adatai + a saját telephely/raktár — konfigurációból (appsettings "Ekaer" szekció).
        // ⚠️ Titkos kulcsok: secret store / appsettings, SOHA NEM a forráskódba.
        services.AddSingleton<INavCredentials>(sp =>
        {
            var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer:Credentials");
            return new NavCredentials
            {
                User = c["User"] ?? "",
                Password = c["Password"] ?? "",
                SigningKey = c["SigningKey"] ?? "",
                TaxNumber = c["TaxNumber"] ?? "",
                BaseUrl = c["BaseUrl"] ?? "https://import-test-b.ekaer.nav.gov.hu", // TEST; PROD: https://import.ekaer.nav.gov.hu
            };
        });

        // EKÁER konfiguráció (cégadat + telephely + küszöb + árfolyam), configból (appsettings "Ekaer").
        // IEkaerSettings-ként kötjük be (AyCode-kontraktus) — a fogyasztók az interfészt kérik, mint az INavCredentials-t.
        services.AddSingleton<IEkaerSettings>(sp =>
        {
            var c = sp.GetRequiredService<IConfiguration>().GetSection("Ekaer");
            var co = c.GetSection("Company");
            var site = co.GetSection("Site");
            return new EkaerSettings
            {
                Company = new EkaerCompanyInfo
                {
                    Name = co["Name"],
                    TaxId = co["TaxId"],
                    CountryCode = co["CountryCode"] ?? "HU",   // a NAV EKÁER magyar
                    PostalCode = co["PostalCode"],
                    City = co["City"],
                    Street = co["Street"],
                    // Telephely = fizikai fel-/lerakodási hely (bejövőnél lerakodás, kimenőnél felrakodás).
                    // Magyar címnél a NAV Name/VatNumber/Phone/Email-t is megköveteli. Ha = székhely, ugyanaz a cím.
                    Site = new LocationType
                    {
                        Name = co["Name"],
                        VatNumber = co["TaxId"],
                        Country = co["CountryCode"] ?? "HU",
                        ZipCode = site["PostalCode"],
                        City = site["City"],
                        Street = site["Street"],
                        Phone = co["Phone"],
                        Email = co["Email"],
                    },
                },

                // Default ÉRTÉK szándékosan nincs: be nem töltött config → 0. A 0 árfolyam a számításkor hibát dob,
                // a 0 küszöb „mindent jelentünk" — egyik sem számol elavult, kódba égetett értékkel.
                EurHufRate = c.GetValue<double>("ExchangeRate:EurHuf"),
                ThresholdWeightKg = c.GetValue<double>("Thresholds:WeightKg"),
                ThresholdValueHuf = c.GetValue<int>("Thresholds:ValueHuf"),
            };
        });

        services.AddControllersWithViews(options =>
        {
            options.Filters.AddService<PendingMeasurementCheckoutFilter>();
        });

        //services.AddLogging(logging =>
        //{
        //    logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Error);
        //    //logging.AddAcLogger(category => new Logger(category)); //Minden .net az aclogger-t használja.
        //});

        services.AddSignalR(hubOptions =>
        {
            hubOptions.EnableDetailedErrors = true;
            hubOptions.MaximumReceiveMessageSize = 30_000_000; // 256 * 1024; unlimited: null;
            hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(FruitBankConstClient.SignalRKeepAliveIntervalSecond);
            hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(FruitBankConstClient.SignarlRTimeoutIntervalSecond);

            //hubOptions.MaximumParallelInvocationsPerClient = 1; //default: 1;
            hubOptions.StatefulReconnectBufferSize = 30_000_000; //30MB; //default: 100,000 bytes;
            //hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(15); //default timeout is 15 seconds
        })
        .AddAcBinaryProtocol(opts =>
        {
            //opts.ProtocolMode = BinaryProtocolMode.AsyncSegment;
            opts.ProtocolMode = BinaryProtocolMode.Bytes;
            opts.FlushPolicy = FlushPolicy.Coalesced;
            opts.FlushTimeout = TimeSpan.FromSeconds(15);

            // Explicit AcLogger instance (name-based category, matches the previous setup).
            // If omitted, the options extension falls back to ILogger<AcBinaryHubProtocol> from DI.
            opts.Logger = new Logger(nameof(AyCodeBinaryHubProtocol));
            // opts.FlushTimeout = TimeSpan.FromSeconds(10);  // default — ~65 KB chunks transfer in ≤9s even on GPRS
        });

        #if DEBUG
        using var tmpSp = services.BuildServiceProvider();
        foreach (var p in tmpSp.GetServices<IHubProtocol>())
            Console.WriteLine($">>> HubProtocol: Name={p.Name} v{p.Version} Assembly={p.GetType().Assembly.GetName().Name}");
        #endif
    }

    /// <summary>
    /// Configure the using of added middleware
    /// </summary>
    /// <param name="application">Builder for configuring an application's request pipeline</param>
    public void Configure(IApplicationBuilder application)
    {
        var fruitBankHubEndPoint = $"/{FruitBankConstClient.DefaultHubName}";
        application.UseWhen(context => context.Request.Path.StartsWithSegments(fruitBankHubEndPoint), app =>
        {
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<DevAdminSignalRHub>(fruitBankHubEndPoint, options =>
                {
                    options.Transports = HttpTransportType.WebSockets;// | HttpTransportType.LongPolling;
                    options.WebSockets.CloseTimeout = TimeSpan.FromSeconds(10); //default: 5 sec.
                    //options.LongPolling.PollTimeout = new TimeSpan(0, 0, 90); //default: 90 sec.

                    options.AllowStatefulReconnects = true;
                    options.TransportMaxBufferSize = 30_000_000; // Increasing this value allows the server to receive larger messages. default: 65KB; unlimited: 0;
                    options.ApplicationMaxBufferSize = 30_000_000; //Increasing this value allows the server to send larger messages. default: 65KB; unlimited: 0;

                    options.TransportSendTimeout = TimeSpan.FromSeconds(60); //default: 10 sec.
                });
            });
        });

        var loggrHubEndPoint = $"/{FruitBankConstClient.LoggerHubName}";
        application.UseWhen(context => context.Request.Path.StartsWithSegments(loggrHubEndPoint), app =>
        {
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapHub<LoggerSignalRHub>(loggrHubEndPoint, options =>
                {
                    options.Transports = HttpTransportType.WebSockets;
                    options.AllowStatefulReconnects = false;
                });
            });
        });
    }

    /// <summary>
    /// Gets order of this startup configuration implementation
    /// </summary>
    public int Order => 4000;
}