using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using FnbEngine.Domain.AggregatesModel.BaristaAggregate;
using FnbEngine.Domain.AggregatesModel.TableAggregate;
using FnbEngine.Domain.AggregatesModel.SessionAggregate;
using FnbEngine.Domain.AggregatesModel.KitchenAggregate;
using FnbEngine.Domain.AggregatesModel.RecipeAggregate;
using FnbEngine.Domain.AggregatesModel.ReservationAggregate;
using FnbEngine.Infrastructure.ExternalServices;
using FnbEngine.Infrastructure.Idempotency;
using FnbEngine.Infrastructure.Repositories;
using Polly;
using Polly.Extensions.Http;

namespace FnbEngine.Infrastructure;

/// <summary>
/// EN: Dependency injection extensions for Infrastructure layer.
/// VI: Extensions dependency injection cho lớp Infrastructure.
/// </summary>
public static class DependencyInjection
{
    /// <summary>
    /// EN: Add infrastructure services to the DI container.
    /// VI: Thêm các services infrastructure vào DI container.
    /// </summary>
    public static IServiceCollection AddInfrastructure(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        // EN: Add DbContext with PostgreSQL / VI: Thêm DbContext với PostgreSQL
        services.AddDbContext<FnbContext>(options =>
        {
            var connectionString = configuration.GetConnectionString("DefaultConnection")
                ?? configuration["DATABASE_URL"]
                ?? throw new InvalidOperationException("Connection string not configured");

            options.UseNpgsql(connectionString, npgsqlOptions =>
            {
                npgsqlOptions.MigrationsAssembly(typeof(FnbContext).Assembly.FullName);
                npgsqlOptions.EnableRetryOnFailure(
                    maxRetryCount: 5,
                    maxRetryDelay: TimeSpan.FromSeconds(30),
                    errorCodesToAdd: null);
            });

            // EN: Enable sensitive data logging in development only
            // VI: Chỉ bật sensitive data logging trong development
            if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
            {
                options.EnableSensitiveDataLogging();
                options.EnableDetailedErrors();
            }
        });

        // EN: Register repositories / VI: Đăng ký repositories
        services.AddScoped<ITableRepository, TableRepository>();
        services.AddScoped<ISessionRepository, SessionRepository>();
        services.AddScoped<IKitchenTicketRepository, KitchenTicketRepository>();
        services.AddScoped<IRecipeRepository, RecipeRepository>();
        services.AddScoped<IReservationRepository, ReservationRepository>();
        services.AddScoped<IBaristaQueueRepository, BaristaQueueRepository>();

        // EN: Register idempotency services / VI: Đăng ký idempotency services
        services.AddScoped<IRequestManager, RequestManager>();

        // EN: Register inventory service HTTP client with Polly retry policy.
        // VI: Đăng ký HTTP client inventory service với Polly retry policy.
        var inventoryServiceUrl = configuration["Services:InventoryService:BaseUrl"]
            ?? configuration["INVENTORY_SERVICE_URL"]
            ?? "http://inventory-service:8080";

        services.AddHttpClient<IInventoryServiceClient, InventoryServiceClient>(client =>
        {
            client.BaseAddress = new Uri(inventoryServiceUrl);
            client.Timeout = TimeSpan.FromSeconds(10);
        })
        .AddPolicyHandler(HttpPolicyExtensions
            .HandleTransientHttpError()
            .WaitAndRetryAsync(3, retryAttempt =>
                TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryAttempt, context) =>
                {
                    // EN: Log retry attempts / VI: Log các lần thử lại
                    Console.WriteLine($"Retry {retryAttempt} for inventory deduction after {timespan.TotalSeconds}s");
                }))
        .AddPolicyHandler(HttpPolicyExtensions
            .HandleTransientHttpError()
            .CircuitBreakerAsync(5, TimeSpan.FromSeconds(30)));

        return services;
    }
}
