// EN: Dependency injection extensions for Infrastructure layer.
// VI: Dependency injection extensions cho Infrastructure layer.

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using IamService.Domain.AggregatesModel.UserAggregate;
using IamService.Domain.AggregatesModel.RoleAggregate;
using IamService.Domain.AggregatesModel.OrganizationAggregate;
using IamService.Domain.AggregatesModel.GroupAggregate;
using IamService.Domain.AggregatesModel.VerificationAggregate;
using IamService.Domain.AggregatesModel.AccessRequestAggregate;
using IamService.Domain.AggregatesModel.AccessReviewAggregate;
using IamService.Domain.AggregatesModel.PrivilegedAccessAggregate;
using IamService.Domain.AggregatesModel.AuditAggregate;
using IamService.Domain.AggregatesModel.ComplianceAggregate;
using IamService.Domain.SeedWork;
using IamService.Infrastructure.Email;
using IamService.Infrastructure.IdentityServer;
using IamService.Infrastructure.Repositories;
using IamService.Infrastructure.SocialLogin;
using IamService.Infrastructure.TwoFactor;

namespace IamService.Infrastructure;

/// <summary>
/// EN: Dependency injection extensions for Infrastructure layer.
/// VI: Dependency injection extensions cho Infrastructure layer.
/// </summary>
public static class DependencyInjection
{
    /// <summary>
    /// EN: Add Infrastructure services to DI container.
    /// VI: Thêm Infrastructure services vào DI container.
    /// </summary>
    public static IServiceCollection AddInfrastructure(
        this IServiceCollection services,
        IConfiguration configuration,
        string? environmentName = null)
    {
        // EN: Get database connection string (optional for Testing environment)
        // VI: Lấy database connection string (tùy chọn cho Testing environment)
        var connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? configuration["DATABASE_URL"];

        // EN: In Testing environment, skip DbContext registration (handled by CustomWebApplicationFactory)
        // VI: Trong Testing environment, bỏ qua đăng ký DbContext (xử lý bởi CustomWebApplicationFactory)
        if (string.Equals(environmentName, "Testing", StringComparison.OrdinalIgnoreCase))
        {
            // EN: Skip DbContext registration - it will be configured in tests
            // VI: Bỏ qua đăng ký DbContext - sẽ được cấu hình trong tests
        }
        else
        {
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new InvalidOperationException("Database connection string not configured");
            }

            // EN: Add DbContext with PostgreSQL
            // VI: Thêm DbContext với PostgreSQL
            services.AddDbContext<IamServiceContext>(options =>
            {
                options.UseNpgsql(connectionString, npgsqlOptions =>
                {
                    npgsqlOptions.MigrationsAssembly(typeof(IamServiceContext).Assembly.FullName);
                    npgsqlOptions.EnableRetryOnFailure(
                        maxRetryCount: 3,
                        maxRetryDelay: TimeSpan.FromSeconds(10),
                        errorCodesToAdd: null);
                });
                // EN: Suppress PendingModelChangesWarning — backing field mappings don't change DB schema.
                // VI: Suppress PendingModelChangesWarning — mapping backing fields không thay đổi DB schema.
                options.ConfigureWarnings(w =>
                    w.Ignore(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.PendingModelChangesWarning));
            });
        }

        // EN: Add ASP.NET Core Identity
        // VI: Thêm ASP.NET Core Identity
        services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
        {
            // EN: Password settings
            // VI: Cài đặt mật khẩu
            options.Password.RequireDigit = true;
            options.Password.RequireLowercase = true;
            options.Password.RequireUppercase = true;
            options.Password.RequireNonAlphanumeric = true;
            options.Password.RequiredLength = 8;

            // EN: Lockout settings
            // VI: Cài đặt khóa tài khoản
            options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
            options.Lockout.MaxFailedAccessAttempts = 5;
            options.Lockout.AllowedForNewUsers = true;

            // EN: User settings
            // VI: Cài đặt user
            options.User.RequireUniqueEmail = true;
            options.SignIn.RequireConfirmedEmail = false;
        })
        .AddEntityFrameworkStores<IamServiceContext>()
        .AddDefaultTokenProviders();

        // EN: Configure Duende IdentityServer
        // VI: Cấu hình Duende IdentityServer
        var issuerUri = configuration["IdentityServer:IssuerUri"];
        
        var identityBuilder = services.AddIdentityServer(options =>
        {
            // EN: Set fixed issuer URI to ensure consistency across hosts and containers
            // VI: Đặt issuer URI cố định để đảm bảo nhất quán giữa host và container
            if (!string.IsNullOrEmpty(issuerUri))
            {
                options.IssuerUri = issuerUri;
            }
            
            // EN: Disable automatic key management (requires Business/Enterprise license)
            // EN: Using AddDeveloperSigningCredential() instead for development
            // VI: Tắt quản lý key tự động (yêu cầu license Business/Enterprise)
            // VI: Sử dụng AddDeveloperSigningCredential() cho môi trường phát triển
            options.KeyManagement.Enabled = false;

            // EN: Events for logging
            // VI: Events để logging
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseSuccessEvents = true;

            // EN: Emit static audience claim
            // VI: Emit static audience claim
            options.EmitStaticAudienceClaim = true;
        })
        .AddInMemoryIdentityResources(Config.IdentityResources)
        .AddInMemoryApiScopes(Config.ApiScopes)
        .AddInMemoryApiResources(Config.ApiResources)
        .AddInMemoryClients(Config.Clients)
        .AddAspNetIdentity<ApplicationUser>()
        .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();

        // EN: Signing credential — dev uses in-memory key, production MUST use a real X.509 certificate.
        // VI: Signing credential — dev dùng key in-memory, production PHẢI dùng X.509 certificate thật.
        if (environmentName == "Development" || string.IsNullOrEmpty(environmentName))
        {
            identityBuilder.AddDeveloperSigningCredential();
        }
        else
        {
            // EN: Read signing certificate path and password from environment variables.
            // VI: Đọc đường dẫn và mật khẩu certificate từ biến môi trường.
            var certPath = configuration["IdentityServer:SigningCertificatePath"]
                ?? throw new InvalidOperationException(
                    "IdentityServer:SigningCertificatePath must be set in non-Development environments. " +
                    "Provide a valid X.509 certificate via environment variable.");
            var certPassword = configuration["IdentityServer:SigningCertificatePassword"]
                ?? throw new InvalidOperationException(
                    "IdentityServer:SigningCertificatePassword must be set in non-Development environments.");
            var cert = System.Security.Cryptography.X509Certificates.X509CertificateLoader.LoadPkcs12FromFile(certPath, certPassword);
            identityBuilder.AddSigningCredential(cert);
        }

        // EN: Add JWT Bearer authentication for API endpoints using local IdentityServer
        // VI: Thêm JWT Bearer authentication cho API endpoints sử dụng IdentityServer cục bộ
        // EN: AddLocalApi() allows JWT validation without external metadata calls (for self-hosted IdentityServer)
        // VI: AddLocalApi() cho phép JWT validation mà không cần gọi metadata bên ngoài (cho self-hosted IdentityServer)
        services.AddAuthentication()
            .AddLocalApi("Bearer", options =>
            {
                options.ExpectedScope = "openid";
            });
        
        // EN: Configure authorization policies
        // VI: Cấu hình authorization policies
        services.AddAuthorization(options =>
        {
            options.AddPolicy("LocalApi", policy =>
            {
                policy.AddAuthenticationSchemes("Bearer");
                policy.RequireAuthenticatedUser();
            });
        });

        // EN: Register repositories
        // VI: Đăng ký repositories
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IRoleRepository, RoleRepository>();
        services.AddScoped<IOrganizationRepository, OrganizationRepository>();
        services.AddScoped<IGroupRepository, GroupRepository>();
        services.AddScoped<IIdentityVerificationRepository, IdentityVerificationRepository>();
        services.AddScoped<IAccessRequestRepository, AccessRequestRepository>();
        services.AddScoped<IAccessReviewRepository, AccessReviewRepository>();
        services.AddScoped<IPrivilegedAccessRepository, PrivilegedAccessRepository>();
        services.AddScoped<IAuditLogRepository, AuditLogRepository>();
        services.AddScoped<IComplianceReportRepository, ComplianceReportRepository>();
        services.AddScoped<IUnitOfWork>(sp => sp.GetRequiredService<IamServiceContext>());

        // EN: Configure Redis caching (skip in Testing environment)
        // VI: Cấu hình Redis caching (bỏ qua trong Testing environment)
        if (!string.Equals(environmentName, "Testing", StringComparison.OrdinalIgnoreCase))
        {
            var redisSettings = new Caching.RedisSettings();
            configuration.GetSection(Caching.RedisSettings.SectionName).Bind(redisSettings);
            
            services.AddSingleton<StackExchange.Redis.IConnectionMultiplexer>(sp =>
            {
                var connString = redisSettings.GetConnectionString();
                return StackExchange.Redis.ConnectionMultiplexer.Connect(connString);
            });
            
            services.AddSingleton<Caching.ICacheService, Caching.RedisCacheService>();
        }

        // EN: Configure Email service
        // VI: Cấu hình Email service
        services.Configure<EmailSettings>(configuration.GetSection(EmailSettings.SectionName));
        services.AddScoped<IEmailService, SmtpEmailService>();

        // EN: Configure 2FA service
        // VI: Cấu hình 2FA service
        services.Configure<TwoFactorSettings>(configuration.GetSection(TwoFactorSettings.SectionName));
        services.AddScoped<ITwoFactorService, TotpTwoFactorService>();

        // EN: Configure Social Login service
        // VI: Cấu hình Social Login service
        services.Configure<SocialLoginSettings>(configuration.GetSection("SocialLogin"));
        services.AddScoped<ISocialLoginService, SocialLoginService>();

        // EN: Configure Google and Facebook authentication
        // VI: Cấu hình Google và Facebook authentication
        var socialLoginSettings = new SocialLoginSettings();
        configuration.GetSection("SocialLogin").Bind(socialLoginSettings);

        if (!string.IsNullOrEmpty(socialLoginSettings.Google?.ClientId))
        {
            services.AddAuthentication()
                .AddGoogle(options =>
                {
                    options.ClientId = socialLoginSettings.Google.ClientId;
                    options.ClientSecret = socialLoginSettings.Google.ClientSecret;
                });
        }

        if (!string.IsNullOrEmpty(socialLoginSettings.Facebook?.AppId))
        {
            services.AddAuthentication()
                .AddFacebook(options =>
                {
                    options.AppId = socialLoginSettings.Facebook.AppId;
                    options.AppSecret = socialLoginSettings.Facebook.AppSecret;
                });
        }

        return services;
    }
}
