using System.Net;
using System.Reflection;
using Discord.Commands;
using Discord.WebSocket;
using FluentValidation;
using Managing.Application;
using Managing.Application.Abstractions;
using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services;
using Managing.Application.Accounts;
using Managing.Application.Agents;
using Managing.Application.Backtests;
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.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.Database.PostgreSql;
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 MediatR;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Orleans.Configuration;

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)
    {
        services.Configure<Web3ProxySettings>(configuration.GetSection("Web3Proxy"));

        return services
                .AddApplication()
                .AddInfrastructure(configuration)
                .AddWorkers(configuration)
                .AddFluentValidation()
                .AddMediatR()
            ;
    }

    // Note: IClusterClient is automatically available in co-hosting scenarios
    // through IGrainFactory. Services should inject IGrainFactory instead of IClusterClient
    // to avoid circular dependency issues during DI container construction.

    public static IHostBuilder ConfigureOrleans(this IHostBuilder hostBuilder, IConfiguration configuration,
        bool isProduction)
    {
        // Check if Orleans grains should be active using root-level configuration
        var runOrleansGrains = configuration.GetValue<bool>("RunOrleansGrains", true);

        // Check environment variable as override
        var runOrleansGrainsEnv = Environment.GetEnvironmentVariable("RUN_ORLEANS_GRAINS");

        if (!string.IsNullOrEmpty(runOrleansGrainsEnv) &&
            bool.TryParse(runOrleansGrainsEnv, out var runOrleansGrainsFromEnv))
        {
            runOrleansGrains = runOrleansGrainsFromEnv;
        }

        // Allow disabling Orleans clustering entirely in case of issues
        var disableOrleansClusteringEnv = Environment.GetEnvironmentVariable("DISABLE_ORLEANS_CLUSTERING");
        var disableOrleansClustering = !string.IsNullOrEmpty(disableOrleansClusteringEnv) && 
                                     bool.TryParse(disableOrleansClusteringEnv, out var disabled) && disabled;

        var postgreSqlConnectionString = configuration.GetSection("PostgreSql")["Orleans"];

        return hostBuilder.UseOrleans(siloBuilder =>
            {
                // Configure clustering with improved networking or use localhost clustering if disabled
                if (!disableOrleansClustering && !string.IsNullOrEmpty(postgreSqlConnectionString))
                {
                    siloBuilder
                        .UseAdoNetClustering(options =>
                        {
                            options.ConnectionString = postgreSqlConnectionString;
                            options.Invariant = "Npgsql";
                        });
                }
                else
                {
                    // Fallback to localhost clustering for testing or when database is unavailable
                    siloBuilder.UseLocalhostClustering();
                }

                // Conditionally configure reminder service based on flag
                if (runOrleansGrains && !disableOrleansClustering && !string.IsNullOrEmpty(postgreSqlConnectionString))
                {
                    siloBuilder.UseAdoNetReminderService(options =>
                    {
                        options.ConnectionString = postgreSqlConnectionString;
                        options.Invariant = "Npgsql";
                    });
                }

                // Configure networking - simplified for Docker compatibility
                if (disableOrleansClustering)
                {
                    // Use localhost clustering when clustering is disabled
                    siloBuilder.ConfigureEndpoints(IPAddress.Loopback, 11111, 30000);
                }
                else
                {
                    // Use localhost for development, proper hostname for production
                    if (isProduction)
                    {
                        // In production, use all interfaces
                        siloBuilder.ConfigureEndpoints(IPAddress.Any, 11111, 30000);
                    }
                    else
                    {
                        // In development, use localhost
                        siloBuilder.ConfigureEndpoints(IPAddress.Loopback, 11111, 30000);
                    }
                }
                
                siloBuilder
                    .Configure<ClusterOptions>(options =>
                    {
                        // Configure cluster options with unique identifiers
                        options.ServiceId = "ManagingApp";
                        options.ClusterId = configuration["ASPNETCORE_ENVIRONMENT"] ?? "Development";
                    })
                    .Configure<MessagingOptions>(options =>
                    {
                        // Configure messaging for better reliability with increased timeouts
                        options.ResponseTimeout = TimeSpan.FromSeconds(60);
                        options.DropExpiredMessages = true;
                    })
                    .Configure<ClusterMembershipOptions>(options =>
                    {
                        // Configure cluster membership for better resilience
                        options.EnableIndirectProbes = true;
                        options.ProbeTimeout = TimeSpan.FromSeconds(10);
                        options.IAmAliveTablePublishTimeout = TimeSpan.FromSeconds(30);
                        options.MaxJoinAttemptTime = TimeSpan.FromSeconds(120);
                        
                        // Improved settings for development environments with stale members
                        options.DefunctSiloCleanupPeriod = TimeSpan.FromMinutes(1);
                        options.DefunctSiloExpiration = TimeSpan.FromMinutes(2);
                    })
                    .Configure<GatewayOptions>(options =>
                    {
                        // Configure gateway with improved timeouts
                        options.GatewayListRefreshPeriod = TimeSpan.FromSeconds(60);
                    });

                // Conditionally configure grain execution based on flag
                if (runOrleansGrains)
                {
                    siloBuilder.Configure<GrainCollectionOptions>(options =>
                    {
                        // Enable grain collection for active grains
                        options.CollectionAge = TimeSpan.FromMinutes(10);
                        options.CollectionQuantum = TimeSpan.FromMinutes(1);
                    });
                }
                else
                {
                    // Disable grain execution completely
                    siloBuilder.Configure<GrainCollectionOptions>(options =>
                    {
                        // Disable grain collection to prevent grains from running
                        options.CollectionAge = TimeSpan.FromDays(365); // Very long collection age
                        options.CollectionQuantum = TimeSpan.FromDays(1); // Very long quantum
                    });
                }

                siloBuilder
                    .ConfigureLogging(logging => logging.SetMinimumLevel(LogLevel.Information));

                // Only enable dashboard in development to avoid shutdown issues
                if (!isProduction)
                {
                    siloBuilder.UseDashboard(options =>
                    {
                        // Configure dashboard with proper shutdown handling
                        options.Port = 9999;
                        options.HostSelf = true;
                        options.CounterUpdateIntervalMs = 10000; // 10 seconds
                        options.HideTrace = true; // Hide trace to reduce dashboard overhead
                        options.Host = "0.0.0.0"; // Allow external connections
                        options.Username = "admin";
                        options.Password = "admin";
                    });
                }

                // Configure grain storage - use ADO.NET for production or memory for fallback
                if (!disableOrleansClustering && !string.IsNullOrEmpty(postgreSqlConnectionString))
                {
                    siloBuilder
                        .AddAdoNetGrainStorage("bot-store", options =>
                        {
                            options.ConnectionString = postgreSqlConnectionString;
                            options.Invariant = "Npgsql";
                        })
                        .AddAdoNetGrainStorage("registry-store", options =>
                        {
                            options.ConnectionString = postgreSqlConnectionString;
                            options.Invariant = "Npgsql";
                        })
                        .AddAdoNetGrainStorage("agent-store", options =>
                        {
                            options.ConnectionString = postgreSqlConnectionString;
                            options.Invariant = "Npgsql";
                        })
                        .AddAdoNetGrainStorage("platform-summary-store", options =>
                        {
                            options.ConnectionString = postgreSqlConnectionString;
                            options.Invariant = "Npgsql";
                        });
                }
                else
                {
                    // Fallback to memory storage when database is unavailable
                    siloBuilder
                        .AddMemoryGrainStorage("bot-store")
                        .AddMemoryGrainStorage("registry-store")
                        .AddMemoryGrainStorage("agent-store")
                        .AddMemoryGrainStorage("platform-summary-store");
                }

                siloBuilder
                    .ConfigureServices(services =>
                    {
                        // Register existing services for Orleans DI
                        // These will be available to grains through dependency injection
                        services.AddTransient<IExchangeService, ExchangeService>();
                        services.AddTransient<IAccountService, AccountService>();
                        services.AddTransient<ITradingService, TradingService>();
                        services.AddTransient<IMessengerService, MessengerService>();
                    });
            })
            ;
    }


    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<IAgentService, AgentService>();
        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>();
        
        // Admin services
        services.AddSingleton<IAdminConfigurationService, AdminConfigurationService>();

        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.AddSingleton<IPrivySettings>(sp =>
            sp.GetRequiredService<IOptions<PrivySettings>>().Value);

        services.Configure<KaigenSettings>(configuration.GetSection("Kaigen"));

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

        // PostgreSql Repositories

        services.AddTransient<IAccountRepository, PostgreSqlAccountRepository>();
        services.AddTransient<IBacktestRepository, PostgreSqlBacktestRepository>();
        services.AddTransient<IGeneticRepository, PostgreSqlGeneticRepository>();
        services.AddTransient<ITradingRepository, PostgreSqlTradingRepository>();
        services.AddTransient<ISettingsRepository, PostgreSqlSettingsRepository>();
        services.AddTransient<IUserRepository, PostgreSqlUserRepository>();
        services.AddTransient<IAgentSummaryRepository, AgentSummaryRepository>();
        services.AddTransient<IStatisticRepository, PostgreSqlStatisticRepository>();
        services.AddTransient<IBotRepository, PostgreSqlBotRepository>();
        services.AddTransient<IWorkerRepository, PostgreSqlWorkerRepository>();
        services.AddTransient<ISynthRepository, PostgreSqlSynthRepository>();

        // InfluxDb Repositories
        services.AddTransient<IInfluxDbRepository, InfluxDbRepository>();
        services.AddTransient<ICandleRepository, CandleRepository>();
        services.AddTransient<IAgentBalanceRepository, AgentBalanceRepository>();

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

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

        return services;
    }

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

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

        // 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>();
        }

        // Other Workers
        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 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>();
        });
    }
}