using Predictalytics.Application.Interfaces;
using Predictalytics.Application.Services;
using Predictalytics.Infrastructure.Services;
using Predictalytics.Domain.Interfaces;
using Predictalytics.Infrastructure.Data;
using Predictalytics.Infrastructure.Data.Repositories;
using Predictalytics.Infrastructure.Providers.Azuro;
using Predictalytics.Infrastructure.Providers.Limitless;
using Predictalytics.Infrastructure.Providers.Polymarket;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Predictalytics.Infrastructure.Configuration;

namespace Predictalytics.Infrastructure;

public static class DependencyInjection
{
    public static IServiceCollection AddPredictalytics(this IServiceCollection services, IConfiguration configuration, string? connectionStringOverride = null, bool dbDebug = false)
    {
        if (dbDebug) Serilog.Log.Warning(">>> INFRASTRUCTURE: AddPredictalytics STARTING");
        
        // MySQL / EF Core
        var connectionString = connectionStringOverride;
        if (string.IsNullOrWhiteSpace(connectionString))
        {
            connectionString = configuration.GetConnectionString("DefaultConnection")
                ?? "Server=localhost;Database=Predictalytics_dev;User=root;Password=;";
        }

        // Use MySqlConnectionStringBuilder to ensure valid format and parse components
        var csBuilder = new MySqlConnector.MySqlConnectionStringBuilder(connectionString);
        
        // Final safety check
        if (string.IsNullOrWhiteSpace(csBuilder.Database))
        {
             Serilog.Log.Error("❌ INVALID CONNECTION STRING: Database name is empty! (Input length: {Length})", connectionString.Length);
             throw new InvalidOperationException("The connection string is missing a valid 'Database' parameter.");
        }

        var maskedCs = csBuilder.ConnectionString.Replace(csBuilder.Password, "****");
        if (dbDebug)
        {
            Serilog.Log.Warning("🗄️ Initializing database connection: {ConnectionString}", maskedCs);
            Serilog.Log.Warning("🗄️ Target Server: {Server}, Database: {Database}", csBuilder.Server, csBuilder.Database);
        }

        // Explicitly register the connection string so we can use it elsewhere if needed
        services.AddSingleton(csBuilder.ConnectionString);

        services.AddDbContext<AppDbContext>(options =>
        {
            // Log exactly what is being used at the moment of configuration
            if (dbDebug) Serilog.Log.Warning("🛠️ EF: Configuring AppDbContext. Target DB: '{Database}'", csBuilder.Database);
            
            options.UseMySql(csBuilder.ConnectionString, new MySqlServerVersion(new Version(8, 0, 31)),
                mysql => 
                {
                    mysql.EnableRetryOnFailure(3, TimeSpan.FromSeconds(10), null);
                    mysql.CommandTimeout(600); // 10 minutes timeout for large migrations (e.g. ALTER TABLE Trades)
                });
        });

        // Repositories
        services.AddScoped<ITraderRepository, TraderRepository>();
        services.AddScoped<ITradeRepository, TradeRepository>();
        services.AddScoped<IMarketRepository, MarketRepository>();
        services.AddScoped<IWatchlistRepository, WatchlistRepository>();
        services.AddScoped<IAlertRepository, AlertRepository>();
        services.AddScoped<IJobRepository, JobRepository>();

        // Application Services
        services.AddScoped<IPositionPnLEngine, PositionPnLEngine>();
        services.AddScoped<IScoringService, ScoringService>();
        services.AddScoped<IDiscoveryService, DiscoveryService>();
        services.AddScoped<IAlertService, AlertService>();
        services.AddScoped<IAnalyticsService, AnalyticsService>();
        services.AddScoped<ICopytradingEstimator, CopytradingEstimator>();
        services.AddScoped<ICopytradingBacktestHarness, CopytradingBacktestHarness>();
        services.AddScoped<WatchlistService>();
        services.AddSingleton<IRateLimiter, RateLimiterService>();
        services.AddSingleton<IPlatformStatisticsService, PlatformStatisticsService>();
        services.AddMemoryCache();

        // Egress pool config & services
        services.Configure<EgressOptions>(configuration.GetSection("Egress"));
        services.AddSingleton<IEgressPoolService, EgressPoolService>();
        services.AddTransient<EgressPoolHandler>();

        // Platform Providers
        services.AddHttpClient();
        services.AddHttpClient("LimitlessApi", c =>
        {
            c.BaseAddress = new Uri("https://api.limitless.exchange/");
            c.DefaultRequestHeaders.Add("Accept", "application/json");
            c.Timeout = TimeSpan.FromSeconds(60);
        });

        services.AddHttpClient("PolymarketData")
            .AddHttpMessageHandler<EgressPoolHandler>();

        services.AddHttpClient("PolymarketGamma")
            .AddHttpMessageHandler<EgressPoolHandler>();

        services.AddHttpClient("PolymarketClob")
            .AddHttpMessageHandler<EgressPoolHandler>();

        services.AddSingleton<PolymarketApiClient>();
        services.AddSingleton<LimitlessApiClient>();
        services.AddHttpClient<Predictalytics.Application.Interfaces.IOpenRouterApiClient, Predictalytics.Infrastructure.Providers.OpenRouter.OpenRouterApiClient>();
        services.AddScoped<Predictalytics.Application.Interfaces.IAiStrategyAnalysisService, Predictalytics.Application.Services.AiStrategyAnalysisService>();
        services.AddSingleton<IPlatformProvider, PolymarketProvider>();
        services.AddSingleton<IPlatformProvider, LimitlessProvider>();
        services.AddSingleton<IPlatformProvider, AzuroProvider>();

        return services;
    }

    /// <summary>
    /// Applies pending EF Core migrations and seeds default platform rows.
    /// Requires the target database to already be "stamped" with the InitialBaseline
    /// migration in __EFMigrationsHistory (see UMSETZUNGSPLAN.md, section B1) if it was
    /// previously created via the old EnsureCreated + manual ALTER approach.
    /// </summary>
    public static async Task EnsureDatabaseAsync(IServiceProvider services, bool dbDebug = false)
    {
        using var scope = services.CreateScope();

        var config = scope.ServiceProvider.GetService<IConfiguration>();
        if (config != null && config.GetValue<bool>("ApiSettings:ReadOnlyDatabase", false))
        {
            Serilog.Log.Warning("⚠️ EnsureDatabaseAsync: Skipping EF Core migrations and platform seeding because the database is configured as Read-Only.");
            return;
        }

        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

        try
        {
            if (dbDebug)
            {
                var maskedConnStr = System.Text.RegularExpressions.Regex.Replace(
                    db.Database.GetDbConnection().ConnectionString ?? "NULL", "Password=[^;]+", "Password=****");
                Serilog.Log.Warning("🔍 DEBUG: Applying EF Core migrations. ConnectionString: {CS}", maskedConnStr);
            }

            await db.Database.MigrateAsync();

            if (dbDebug) Serilog.Log.Warning("✅ DEBUG: Migrations applied successfully.");

            // Seed default platform rows (idempotent)
            var conn = db.Database.GetDbConnection();
            if (conn.State != System.Data.ConnectionState.Open) await conn.OpenAsync();

            using var seedPlatform = conn.CreateCommand();
            seedPlatform.CommandText = @"
                INSERT IGNORE INTO `PlatformConfigs` (`Id`, `Name`, `DisplayName`, `IsActive`, `CreatedAt`, `UpdatedAt`) VALUES
                (0, 'Unknown', 'Unknown Platform', 1, UTC_TIMESTAMP(), UTC_TIMESTAMP()),
                (1, 'Polymarket', 'Polymarket', 1, UTC_TIMESTAMP(), UTC_TIMESTAMP()),
                (2, 'Azuro', 'Azuro', 1, UTC_TIMESTAMP(), UTC_TIMESTAMP()),
                (3, 'Limitless', 'Limitless', 1, UTC_TIMESTAMP(), UTC_TIMESTAMP());";
            await seedPlatform.ExecuteNonQueryAsync();

            await conn.CloseAsync();
        }
        catch (Exception ex)
        {
            Serilog.Log.Warning("⚠️ Could not connect to database or apply migrations: {Message}. Background workers will retry connection automatically.", ex.Message);
        }
    }
}
