using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MktZaloService.Domain.AggregatesModel.ChatbotRuleAggregate;
using MktZaloService.Domain.AggregatesModel.ConversationAggregate;
using MktZaloService.Domain.AggregatesModel.CustomerAggregate;
using MktZaloService.Domain.AggregatesModel.TemplateAggregate;
using MktZaloService.Infrastructure.Caching;
using MktZaloService.Infrastructure.Idempotency;
using MktZaloService.Infrastructure.Repositories;
using MktZaloService.Infrastructure.Zalo;
using Polly;
using Polly.Extensions.Http;

namespace MktZaloService.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<MktZaloServiceContext>(options =>
        {
            var connectionString = configuration.GetConnectionString("DefaultConnection")
                ?? configuration["DATABASE_URL"]
                ?? throw new InvalidOperationException("Connection string not configured");

            options.UseNpgsql(connectionString, npgsqlOptions =>
            {
                npgsqlOptions.MigrationsAssembly(typeof(MktZaloServiceContext).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<IConversationRepository, ConversationRepository>();
        services.AddScoped<ICustomerRepository, CustomerRepository>();
        services.AddScoped<IChatbotRuleRepository, ChatbotRuleRepository>();
        services.AddScoped<IMessageTemplateRepository, MessageTemplateRepository>();

        // EN: Register Zalo OA services / VI: Đăng ký các services Zalo OA
        services.Configure<ZaloOAOptions>(configuration.GetSection(ZaloOAOptions.SectionName));
        services.AddSingleton<IZaloWebhookVerifier, ZaloWebhookVerifier>();

        // EN: Register Zalo HTTP client with Polly retry policies
        // VI: Đăng ký Zalo HTTP client với Polly retry policies
        var zaloOptions = configuration.GetSection(ZaloOAOptions.SectionName).Get<ZaloOAOptions>()
            ?? new ZaloOAOptions();

        services.AddHttpClient<IZaloOfficialAccountClient, ZaloOfficialAccountClient>(client =>
        {
            client.BaseAddress = new Uri(zaloOptions.BaseUrl);
            client.Timeout = TimeSpan.FromSeconds(zaloOptions.TimeoutSeconds);
        })
        .AddPolicyHandler(GetRetryPolicy(zaloOptions.MaxRetryAttempts))
        .AddPolicyHandler(GetCircuitBreakerPolicy());

        // EN: Register Redis distributed cache / VI: Đăng ký Redis distributed cache
        var redisConnection = configuration.GetConnectionString("Redis")
            ?? configuration["REDIS_URL"];

        if (!string.IsNullOrEmpty(redisConnection))
        {
            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = redisConnection;
                options.InstanceName = "MktZaloService:";
            });
        }
        else
        {
            // EN: Fallback to in-memory cache for development
            // VI: Dùng in-memory cache cho development
            services.AddDistributedMemoryCache();
        }

        // EN: Register cache services / VI: Đăng ký cache services
        services.AddScoped<IConversationCacheService, ConversationCacheService>();
        services.AddScoped<IChatbotRuleCacheService, ChatbotRuleCacheService>();

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

        return services;
    }

    /// <summary>
    /// EN: Get retry policy for HTTP requests.
    /// VI: Lấy chính sách retry cho HTTP requests.
    /// </summary>
    private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy(int maxRetryAttempts)
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .OrResult(msg => (int)msg.StatusCode >= 500)
            .WaitAndRetryAsync(
                maxRetryAttempts,
                retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)),
                onRetry: (outcome, timespan, retryAttempt, context) =>
                {
                    // EN: Log retry attempts / VI: Log các lần retry
                    Console.WriteLine($"Retry {retryAttempt} after {timespan.TotalSeconds}s");
                });
    }

    /// <summary>
    /// EN: Get circuit breaker policy for HTTP requests.
    /// VI: Lấy chính sách circuit breaker cho HTTP requests.
    /// </summary>
    private static IAsyncPolicy<HttpResponseMessage> GetCircuitBreakerPolicy()
    {
        return HttpPolicyExtensions
            .HandleTransientHttpError()
            .CircuitBreakerAsync(
                handledEventsAllowedBeforeBreaking: 5,
                durationOfBreak: TimeSpan.FromSeconds(30));
    }
}
