using Managing.Application;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Accounts;
using Managing.Application.Backtests;
using Managing.Application.ManageBot;
using Managing.Application.MoneyManagements;
using Managing.Application.Scenarios;
using Managing.Application.Shared;
using Managing.Application.Synth;
using Managing.Application.Trading;
using Managing.Application.Trading.Commands;
using Managing.Application.Trading.Handlers;
using Managing.Application.Users;
using Managing.Application.Workers;
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.PostgreSql;
using Managing.Infrastructure.Databases.PostgreSql.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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Managing.Bootstrap;

public static class WorkersBootstrap
{
    public static IServiceCollection RegisterWorkersDependencies(this IServiceCollection services,
        IConfiguration configuration)
    {
        return services
            .AddApplication()
            .AddInfrastructure(configuration)
            .AddWorkers(configuration);
    }

    private static IServiceCollection AddApplication(this IServiceCollection services)
    {
        services.AddScoped<ITradingService, TradingService>();
        services.AddScoped<IScenarioService, ScenarioService>();
        services.AddScoped<IMoneyManagementService, MoneyManagementService>();
        services.AddScoped<IAccountService, AccountService>();
        services.AddScoped<IStatisticService, StatisticService>();
        services.AddScoped<ISettingsService, SettingsService>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IGeneticService, GeneticService>();
        services.AddScoped<IBotService, BotService>();
        services.AddScoped<IWorkerService, WorkerService>();
        services.AddScoped<ISynthPredictionService, SynthPredictionService>();
        services.AddScoped<ISynthApiClient, SynthApiClient>();
        services.AddScoped<IPricesService, PricesService>();
        services.AddTransient<ICommandHandler<OpenPositionRequest, Position>, OpenPositionCommandHandler>();
        services.AddTransient<ICommandHandler<ClosePositionCommand, Position>, ClosePositionCommandHandler>();

        // Processors
        services.AddTransient<IBacktester, Backtester>();
        services.AddTransient<IExchangeProcessor, EvmProcessor>();

        services.AddTransient<ITradaoService, TradaoService>();
        services.AddTransient<IExchangeService, ExchangeService>();
        services.AddTransient<IExchangeStream, ExchangeStream>();


        services.AddTransient<IPrivyService, PrivyService>();
        services.AddTransient<IWeb3ProxyService, Web3ProxyService>();
        services.AddTransient<IWebhookService, WebhookService>();
        services.AddTransient<IKaigenService, KaigenService>();

        services.AddSingleton<IMessengerService, MessengerService>();
        services.AddSingleton<IDiscordService, DiscordService>();

        return services;
    }

    private static IServiceCollection AddWorkers(this IServiceCollection services, IConfiguration configuration)
    {
        // Price Workers
        if (configuration.GetValue<bool>("WorkerPricesFifteenMinutes", false))
        {
            services.AddHostedService<PricesFifteenMinutesWorker>();
        }

        if (configuration.GetValue<bool>("WorkerPricesOneHour", false))
        {
            services.AddHostedService<PricesOneHourWorker>();
        }

        if (configuration.GetValue<bool>("WorkerPricesFourHours", false))
        {
            services.AddHostedService<PricesFourHoursWorker>();
        }

        if (configuration.GetValue<bool>("WorkerPricesOneDay", false))
        {
            services.AddHostedService<PricesOneDayWorker>();
        }

        if (configuration.GetValue<bool>("WorkerPricesFiveMinutes", false))
        {
            services.AddHostedService<PricesFiveMinutesWorker>();
        }

        if (configuration.GetValue<bool>("WorkerSpotlight", false))
        {
            services.AddHostedService<SpotlightWorker>();
        }

        if (configuration.GetValue<bool>("WorkerTraderWatcher", false))
        {
            services.AddHostedService<TraderWatcher>();
        }

        if (configuration.GetValue<bool>("WorkerLeaderboard", false))
        {
            services.AddHostedService<LeaderboardWorker>();
        }

        if (configuration.GetValue<bool>("WorkerFundingRatesWatcher", false))
        {
            services.AddHostedService<FundingRatesWatcher>();
        }

        if (configuration.GetValue<bool>("WorkerGeneticAlgorithm", false))
        {
            services.AddHostedService<GeneticAlgorithmWorker>();
        }

        if (configuration.GetValue<bool>("WorkerBundleBacktest", false))
        {
            services.AddHostedService<BundleBacktestWorker>();
        }

        return services;
    }

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

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

        services.AddTransient<IInfluxDbRepository, InfluxDbRepository>();

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

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

        // Repositories
        services.AddTransient<ICandleRepository, CandleRepository>();
        services.AddTransient<IAgentBalanceRepository, AgentBalanceRepository>();
        services.AddTransient<IWorkerRepository, PostgreSqlWorkerRepository>();
        services.AddTransient<IStatisticRepository, PostgreSqlStatisticRepository>();
        services.AddTransient<ICandleRepository, CandleRepository>();
        services.AddTransient<IAccountRepository, PostgreSqlAccountRepository>();
        services.AddTransient<ISettingsRepository, PostgreSqlSettingsRepository>();
        services.AddTransient<ITradingRepository, PostgreSqlTradingRepository>();
        services.AddTransient<IBacktestRepository, PostgreSqlBacktestRepository>();
        services.AddTransient<IBotRepository, PostgreSqlBotRepository>();
        services.AddTransient<IUserRepository, PostgreSqlUserRepository>();
        services.AddTransient<ISynthRepository, PostgreSqlSynthRepository>();
        services.AddTransient<IGeneticRepository, PostgreSqlGeneticRepository>();

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


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

        // Web Clients
        services.AddTransient<ITradaoService, TradaoService>();
        services.AddTransient<IExchangeService, ExchangeService>();
        services.AddSingleton<IPrivyService, PrivyService>();
        services.AddSingleton<ISynthApiClient, SynthApiClient>();

        // Web3Proxy Configuration
        services.Configure<Web3ProxySettings>(configuration.GetSection("Web3Proxy"));
        services.AddTransient<IWeb3ProxyService, Web3ProxyService>();

        // Http Clients
        services.AddHttpClient();

        // Messengers
        services.AddSingleton<IMessengerService, MessengerService>();
        services.AddSingleton<IDiscordService, DiscordService>();
        services.AddSingleton<IWebhookService, WebhookService>();

        return services;
    }
}