using System.Reflection;
using Binance.Net.Clients;
using Binance.Net.Interfaces.Clients;
using Discord.Commands;
using Discord.WebSocket;
using FluentValidation;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Accounts;
using Managing.Application.Backtesting;
using Managing.Application.Bots.Base;
using Managing.Application.ManageBot;
using Managing.Application.ManageBot.Commands;
using Managing.Application.MoneyManagements;
using Managing.Application.Scenarios;
using Managing.Application.Shared;
using Managing.Application.Shared.Behaviours;
using Managing.Application.Trading;
using Managing.Application.Trading.Commands;
using Managing.Application.Users;
using Managing.Application.Workers;
using Managing.Application.Workers.Abstractions;
using Managing.Application.Workflows;
using Managing.Domain.Trades;
using Managing.Infrastructure.Databases;
using Managing.Infrastructure.Databases.InfluxDb;
using Managing.Infrastructure.Databases.InfluxDb.Abstractions;
using Managing.Infrastructure.Databases.InfluxDb.Models;
using Managing.Infrastructure.Databases.MongoDb;
using Managing.Infrastructure.Databases.MongoDb.Abstractions;
using Managing.Infrastructure.Databases.MongoDb.Configurations;
using Managing.Infrastructure.Evm;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Evm.Models.Privy;
using Managing.Infrastructure.Evm.Services;
using Managing.Infrastructure.Evm.Subgraphs;
using Managing.Infrastructure.Exchanges;
using Managing.Infrastructure.Exchanges.Abstractions;
using Managing.Infrastructure.Exchanges.Exchanges;
using Managing.Infrastructure.Messengers.Discord;
using Managing.Infrastructure.Storage;
using MediatR;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Managing.Bootstrap;

public static class ApiBootstrap
{
    private static readonly Assembly ApplicationAssembly = typeof(StartBotCommand).GetTypeInfo().Assembly;

    public static IServiceCollection RegisterApiDependencies(this IServiceCollection services,
        IConfiguration configuration)
    {
        return services
            .AddApplication()
            .AddInfrastructure(configuration)
            .AddFluentValidation()
            .AddMediatR();
    }

    private static IServiceCollection AddApplication(this IServiceCollection services)
    {
        services.AddSingleton<ITradingService, TradingService>();
        services.AddSingleton<IBotFactory, BotFactory>();
        services.AddSingleton<IBacktester, Backtester>();
        services.AddSingleton<IScenarioService, ScenarioService>();
        services.AddSingleton<IMoneyManagementService, MoneyManagementService>();
        services.AddSingleton<IAccountService, AccountService>();
        services.AddSingleton<IStatisticService, StatisticService>();
        services.AddSingleton<ISettingsService, SettingsService>();
        services.AddSingleton<IUserService, UserService>();
        services.AddSingleton<IWorkflowService, WorkflowService>();
        services.AddSingleton<IFlowFactory, FlowFactory>();
        services.AddTransient<ICommandHandler<OpenPositionRequest, Position>, OpenPositionCommandHandler>();
        services.AddTransient<ICommandHandler<ClosePositionCommand, Position>, ClosePositionCommandHandler>();

        return services;
    }

    private static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    {
        // Database
        services.AddSingleton<IManagingDatabaseSettings>(sp =>
            sp.GetRequiredService<IOptions<ManagingDatabaseSettings>>().Value);

        services.AddTransient(typeof(IMongoRepository<>), typeof(MongoRepository<>));

        services.AddSingleton<IInfluxDbSettings>(sp =>
            sp.GetRequiredService<IOptions<InfluxDbSettings>>().Value);

        services.AddSingleton<IPrivySettings>(sp =>
            sp.GetRequiredService<IOptions<PrivySettings>>().Value);

        // Evm 
        services.AddGbcFeed();
        services.AddUniswapV2();
        services.AddChainlink();
        services.AddChainlinkGmx();
        services.AddSingleton<IEvmManager, EvmManager>();

        // Repositories
        services.AddTransient<IInfluxDbRepository, InfluxDbRepository>();
        services.AddTransient<ICandleRepository, CandleRepository>();
        services.AddTransient<IAccountRepository, AccountRepository>();
        services.AddTransient<IBacktestRepository, BacktestRepository>();
        services.AddTransient<ITradingRepository, TradingRepository>();
        services.AddTransient<ISettingsRepository, SettingsRepository>();
        services.AddTransient<IUserRepository, UserRepository>();
        services.AddTransient<IStatisticRepository, StatisticRepository>();
        services.AddTransient<IWorkflowRepository, WorkflowRepository>();
        services.AddTransient<IBotRepository, BotRepository>();
        services.AddTransient<IWorkerRepository, WorkerRepository>();

        // Cache
        services.AddDistributedMemoryCache();
        services.AddTransient<ICacheService, CacheService>();
        services.AddSingleton<ITaskCache, TaskCache>();

        // Processors
        services.AddTransient<IExchangeProcessor, EvmProcessor>();
        services.AddTransient<IExchangeProcessor, FtxProcessor>();
        services.AddTransient<IExchangeProcessor, BinanceProcessor>();
        services.AddTransient<IExchangeProcessor, KrakenProcessor>();

        // Services
        services.AddTransient<ITradaoService, TradaoService>();
        services.AddTransient<IExchangeService, ExchangeService>();
        services.AddTransient<IExchangeStream, ExchangeStream>();
        services.AddSingleton<IMessengerService, MessengerService>();
        services.AddSingleton<IDiscordService, DiscordService>();
        services.AddSingleton<IBotService, BotService>();
        services.AddSingleton<IWorkerService, WorkerService>();
        services.AddTransient<IPrivyService, PrivyService>();
        
        // Web3Proxy Configuration
        services.Configure<Web3ProxySettings>(configuration.GetSection("Web3Proxy"));
        services.AddTransient<IWeb3ProxyService, Web3ProxyService>();

        // Stream
        services.AddSingleton<IBinanceSocketClient, BinanceSocketClient>();
        services.AddSingleton<IStreamService, StreamService>();

        return services;
    }

    private static IServiceCollection AddMediatR(this IServiceCollection services)
    {
        return services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(AppDomain.CurrentDomain.GetAssemblies()))
            .AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehaviour<,>))
            .AddTransient(typeof(IUnhandledExceptionBehaviour<,>), typeof(UnhandledExceptionBehaviour<,>));
    }

    private static IServiceCollection AddFluentValidation(this IServiceCollection services)
    {
        return services.AddValidatorsFromAssembly(ApplicationAssembly);
    }

    public static IWebHostBuilder SetupDiscordBot(this IWebHostBuilder builder)
    {
        return builder.ConfigureServices(services =>
        {
            services
                .AddSingleton<DiscordSettings>()
                .AddSingleton<DiscordSocketClient>()
                .AddSingleton<CommandService>()
                .AddHostedService<DiscordService>();
        });
    }
}