using EVCB_OCPP.Domain;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;

namespace EVCB_OCPP.WEBAPI.Helpers;

public static class AddPortalDbContext
{
    public const string CommandTimeoutKey = "CommandTimeout";
    public static IServiceCollection AddMainDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "MainDbUserIdKey";
        const string DbPassKey = "MainDbPass";
        const string DbConnectionStringKey = "MainDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);

        services.AddSingleton(
            new SqlConnectionFactory<MainDBContext>()
            {
                ConnectionString = conneciotnString
            });
        AddPortalDbContextInternal<MainDBContext>(services, configuration, conneciotnString, logToConsole: false);
        return services;
    }

    public static IServiceCollection AddMeterValueDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "MeterValueDbUserId";
        const string DbPassKey = "MeterValueDbPass";
        const string DbConnectionStringKey = "MeterValueDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
        services.AddSingleton(
            new SqlConnectionFactory<MeterValueDBContext>()
            {
                ConnectionString = conneciotnString
            });
        AddPortalDbContextInternal<MeterValueDBContext>(services, configuration, conneciotnString, logToConsole: false);
        return services;
    }

    public static IServiceCollection AddConnectionLogDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "ConnectionLogDbUserId";
        const string DbPassKey = "ConnectionLogDbPass";
        const string DbConnectionStringKey = "ConnectionLogDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
        services.AddSingleton(
            new SqlConnectionFactory<ConnectionLogDBContext>()
            {
                ConnectionString = conneciotnString
            });
        AddPortalDbContextInternal<ConnectionLogDBContext>(services, configuration, conneciotnString);
        return services;
    }

    public static IServiceCollection AddWebDBConetext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "WebDbUserId";
        const string DbPassKey = "WebDbPass";
        const string DbConnectionStringKey = "WebDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
        services.AddSingleton(
            new SqlConnectionFactory<WebDBConetext>()
            {
                ConnectionString = conneciotnString
            });
        return services;
    }

    public static IServiceCollection AddOnlineLogDBContext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "OnlineLogDbUserId";
        const string DbPassKey = "OnlineLogDbPass";
        const string DbConnectionStringKey = "OnlineLogDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
        services.AddSingleton(
            new SqlConnectionFactory<OnlineLogDBContext>()
            {
                ConnectionString = conneciotnString
            });
        return services;
    }
    
    public static IServiceCollection AddAPILogDBContext(this IServiceCollection services, IConfiguration configuration)
    {
        const string DbUserIdKey = "APILogDbUserId";
        const string DbPassKey = "APILogDbPass";
        const string DbConnectionStringKey = "APILogDBContext";

        var conneciotnString = GetConnectionString(configuration, DbUserIdKey, DbPassKey, DbConnectionStringKey);
        services.AddSingleton(
            new SqlConnectionFactory<APILogDBContext>()
            {
                ConnectionString = conneciotnString
            });
        return services;
    }

    private static void AddPortalDbContextInternal<T>(
        IServiceCollection services, IConfiguration configuration,
        string connectionString, bool logToConsole = false) where T : DbContext
    {

        var commandTimeout = int.TryParse(configuration[CommandTimeoutKey], out var temp) ? temp : 180;

        services.AddPooledDbContextFactory<T>((serviceProvider, options) => {
            options.UseSqlServer(connectionString, dbOptions =>
            {
                dbOptions.CommandTimeout(commandTimeout);
            });
            if (logToConsole)
            {
                options.LogTo(Console.WriteLine);
            }
        });
    }

    private static string GetConnectionString(IConfiguration configuration, string UserIdKey, string DbPassKey, string ConnectionStringKey)
    {
        string mainDbUserId = string.IsNullOrEmpty(configuration[UserIdKey]) ? string.Empty : $"user id={configuration[UserIdKey]};";
        string mainDbUserPass = string.IsNullOrEmpty(configuration[DbPassKey]) ? string.Empty : $"password={configuration[DbPassKey]};";
        return $"{configuration.GetConnectionString(ConnectionStringKey)}{mainDbUserId}{mainDbUserPass}";
    }
}

public class SqlConnectionFactory<T> where T : DbContext
{
    public string ConnectionString { get; init; }
    public SqlConnectionFactory() { }
    public SqlConnection Create()
    {
        var sqlConnection = new SqlConnection(ConnectionString);
        sqlConnection.Open();
        return sqlConnection;
    }

    public async Task<SqlConnection> CreateAsync()
    {
        var sqlConnection = new SqlConnection(ConnectionString);
        await sqlConnection.OpenAsync();
        return sqlConnection;
    }
}

/// <summary>
/// Dummy
/// </summary>
public class WebDBConetext : DbContext { }

/// <summary>
/// Dummy
/// </summary>
public class OnlineLogDBContext : DbContext { }
/// <summary>
/// Dummy
/// </summary>
public class APILogDBContext : DbContext { }
