using SharedKernel;
using Infrastructure.Persistence;
using Infrastructure.Persistence.Identity;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Caching.Distributed;
using System.Net;
using IPNetwork = Microsoft.AspNetCore.HttpOverrides.IPNetwork;

namespace Admin.Extensions
{
    public static class ServiceCollectionExtensions
    {
        public static void AddAdminForwardedHeaders(this IServiceCollection services, AppSettings settings)
        {
            services.Configure<ForwardedHeadersOptions>(options =>
            {
                options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost;
                options.ForwardLimit = settings.ForwardedHeaders.ForwardLimit <= 0 ? 1 : settings.ForwardedHeaders.ForwardLimit;

                // 설정에서 KnownProxies / KnownNetworks 읽기
                var knownProxies = settings.ForwardedHeaders.KnownProxies;
                if (knownProxies != null)
                {
                    foreach (var proxy in knownProxies)
                    {
                        if (IPAddress.TryParse(proxy, out var ip))
                        {
                            options.KnownProxies.Add(ip);
                        }
                    }
                }

                var knownNetworks = settings.ForwardedHeaders.KnownNetworks;
                if (knownNetworks != null)
                {
                    foreach (var net in knownNetworks)
                    {
                        var parts = net.Split('/');
                        if (parts.Length == 2 && IPAddress.TryParse(parts[0], out var networkIp) && int.TryParse(parts[1], out var prefix))
                        {
                            options.KnownNetworks.Add(new IPNetwork(networkIp, prefix));
                        }
                    }
                }
            });
        }

        // 관리자단 비밀번호 정책 구성
        public static void AddAdminIdentity(this IServiceCollection services, AppSettings settings)
        {
            services.AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.SignIn.RequireConfirmedAccount = true; // 이메일 확인 활성화

                // Password settings.
                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = true;
                options.Password.RequireUppercase = true;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;

                // Lockout settings.
                options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
                options.Lockout.MaxFailedAccessAttempts = 5;
                options.Lockout.AllowedForNewUsers = true;

                // User settings.
                options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";
                options.User.RequireUniqueEmail = true;
            })
            .AddDefaultUI() // 기본 UI 사용
            .AddDefaultTokenProviders() // 기본 토큰 제공자 사용
            .AddEntityFrameworkStores<IdentityDbContext>(); // 사용자 계정 저장소

            // Identity Cookie Session Store 설정
            services.AddSingleton<ITicketStore>(sp =>
                new DistributedCacheTicketStore(
                    sp.GetRequiredService<IDistributedCache>(),
                    keyPrefix: settings.Redis.AuthTicketPrefix
                )
            );

            services.AddOptions<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme).Configure<ITicketStore>((options, ticketStore) =>
            {
                options.SessionStore = ticketStore;
            });

            services.ConfigureApplicationCookie(options =>
            {
                // Cookie settings
                options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
                options.LoginPath = "/Identity/Account/Login";
                options.AccessDeniedPath = "/Identity/Account/AccessDenied";
                options.SlidingExpiration = true;

                options.Events.OnRedirectToLogin = context =>
                {
                    if (context.Request.Path.StartsWithSegments("/Api"))
                    {
                        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        return Task.CompletedTask;
                    }

                    context.Response.Redirect(context.RedirectUri); // 기존 쿠키 인증 흐름 유지
                    return Task.CompletedTask;
                };

                options.Events.OnRedirectToAccessDenied = context =>
                {
                    if (context.Request.Path.StartsWithSegments("/Api"))
                    {
                        context.Response.StatusCode = StatusCodes.Status403Forbidden;
                        return Task.CompletedTask;
                    }

                    context.Response.Redirect(context.RedirectUri);
                    return Task.CompletedTask;
                };
            });
        }
    }
}