using EVCB_OCPP.Domain.ConnectionFactory;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EVCB_OCPP.Domain.Extensions;

public static class IServiceCollectionExtension
{
    public const string CommandTimeoutKey = "CommandTimeout";

    public const string MainDbUserIdKey = "MainDbUserIdKey";
    public const string MainDbPassKey = "MainDbPass";
    public const string MainDbConnectionStringKey = "MainDBContext";

    public const string MeterDbUserIdKey = "MeterValueDbUserId";
    public const string MeterDbPassKey = "MeterValueDbPass";
    public const string MeterDbConnectionStringKey = "MeterValueDBContext";

    public const string ConnectionLogDbUserIdKey = "ConnectionLogDbUserId";
    public const string ConnectionLogDbPassKey = "ConnectionLogDbPass";
    public const string ConnectionLogDbConnectionStringKey = "ConnectionLogDBContext";

    public const string WebDbUserIdKey = "WebDbUserId";
    public const string WebDbPassKey = "WebDbPass";
    public const string WebDbConnectionStringKey = "WebDBContext";

    public const string OnlineLogDbUserIdKey = "OnlineLogDbUserId";
    public const string OnlineLogDbPassKey = "OnlineLogDbPass";
    public const string OnlineLogDbConnectionStringKey = "OnlineLogDBContext";

    public const string ApiLogDbUserIdKey = "APILogDbUserId";
    public const string ApiLogDbPassKey = "APILogDbPass";
    public const string ApiLogDbConnectionStringKey = "APILogDBContext";


    public static IServiceCollection AddMainDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(MainDbUserIdKey, MainDbPassKey, MainDbConnectionStringKey);

        services.AddSqlConnectionFactory<MainDBContext>(conneciotnString);
        AddPortalDbContextInternal<MainDBContext>(services, configuration, conneciotnString, logToConsole: false);
        return services;
    }

    public static IServiceCollection AddMeterValueDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(MeterDbUserIdKey, MeterDbPassKey, MeterDbConnectionStringKey);
        services.AddSqlConnectionFactory<MeterValueDBContext>(conneciotnString);
        AddPortalDbContextInternal<MeterValueDBContext>(services, configuration, conneciotnString, logToConsole: false);
        return services;
    }

    public static IServiceCollection AddConnectionLogDbContext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(ConnectionLogDbUserIdKey, ConnectionLogDbPassKey, ConnectionLogDbConnectionStringKey);
        services.AddSqlConnectionFactory<ConnectionLogDBContext>(conneciotnString);
        AddPortalDbContextInternal<ConnectionLogDBContext>(services, configuration, conneciotnString, logToConsole: false);
        return services;
    }

    public static IServiceCollection AddWebDBConetext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(WebDbUserIdKey, WebDbPassKey, WebDbConnectionStringKey);
        services.AddSqlConnectionFactory<WebDBConetext>(conneciotnString);
        return services;
    }

    public static IServiceCollection AddOnlineLogDBContext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(OnlineLogDbUserIdKey, OnlineLogDbPassKey, OnlineLogDbConnectionStringKey);
        services.AddSqlConnectionFactory<OnlineLogDBContext>(conneciotnString);
        return services;
    }
    public static IServiceCollection AddAPILogDBContext(this IServiceCollection services, IConfiguration configuration)
    {
        var conneciotnString = configuration.GetConnectionString(ApiLogDbUserIdKey, ApiLogDbPassKey, ApiLogDbConnectionStringKey);
        services.AddSqlConnectionFactory<APILogDBContext>(conneciotnString);
        return services;
    }

    public static void AddSqlConnectionFactory<T>(this IServiceCollection services, string conneciotnString) where T : DbContext
    {
        services.AddSingleton<ISqlConnectionFactory<T>>(
            (serviceProvider) =>
            new SqlConnectionFactory<T>(serviceProvider.GetRequiredService<ILogger<SqlConnectionFactory<T>>>())
            {
                ConnectionString = conneciotnString
            });
    }

    public 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);
            });
            options.UseLoggerFactory(serviceProvider.GetRequiredService<ILoggerFactory>());
        });
    }

    public static string GetConnectionString(this IConfiguration configuration, string UserIdKey, string DbPassKey, string ConnectionStringKey)
    {
        string dbUserId = string.IsNullOrEmpty(configuration[UserIdKey]) ? string.Empty : $"user id={configuration[UserIdKey]};";
        string dbUserPass = string.IsNullOrEmpty(configuration[DbPassKey]) ? string.Empty : $"password={configuration[DbPassKey]};";
        string connectionString = configuration.GetConnectionString(ConnectionStringKey);

        var builder = new SqlConnectionStringBuilder(connectionString);
        if (!string.IsNullOrEmpty(dbUserId))
        {
            builder.UserID = dbUserId;
        }
        if (!string.IsNullOrEmpty(dbUserPass))
        {
            builder.Password = dbUserPass;
        }

        return builder.ToString();
    }
}