using SharedKernel.Constants;
using Application.Abstractions.Data;
using Domain.Entities.Director;
using System.Diagnostics;

namespace Admin.Middlewares;

public class AdminAccessLogMiddleware(RequestDelegate next)
{
    // 로깅 제외 경로
    private static readonly string[] ExcludePrefixes =
    [
        "/lib/",
        "/css/",
        "/js/",
        "/images/",
        "/favicon",
        "/_framework",
        "/_blazor",
        "/.well-known/",
        "/Identity/Account"
    ];

    // 로깅 제외 확장자
    private static readonly string[] ExcludeExtensions =
    [
        ".css",
        ".js",
        ".map",
        ".png",
        ".jpg",
        ".jpeg",
        ".gif",
        ".svg",
        ".ico",
        ".woff",
        ".woff2",
        ".ttf",
        ".eot"
    ];

    public async Task InvokeAsync(HttpContext context)
    {
        var path = context.Request.Path.Value ?? "";

        // 정적 파일, Identity 페이지 제외
        if (ShouldSkip(path))
        {
            await next(context);
            return;
        }

        // 인증되지 않은 사용자 제외
        if (context.User.Identity is not { IsAuthenticated: true })
        {
            await next(context);
            return;
        }

        var sw = Stopwatch.StartNew(); // 시작 시간

        await next(context);

        sw.Stop(); // 종료 시간

        try
        {
            var db = context.RequestServices.GetRequiredService<IAppDbContext>();

            var userID = context.User.Identity.Name ?? "-";
            var userName = context.User.FindFirst(System.Security.Claims.ClaimTypes.GivenName)?.Value;
            var method = context.Request.Method;
            var queryString = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : null;
            var statusCode = context.Response.StatusCode;
            var elapsedMs = sw.ElapsedMilliseconds;
            var menuName = ResolveMenuName(path);
            var ipAddress = context.Connection.RemoteIpAddress?.ToString();
            var userAgent = context.Request.Headers.UserAgent.ToString();

            var log = AdminAccessLog.Create(
                userID,
                userName,
                method,
                path,
                queryString,
                statusCode,
                elapsedMs,
                menuName,
                ipAddress,
                userAgent);

            await db.AdminAccessLog.AddAsync(log);
            await db.SaveChangesAsync();
        }
        catch
        {

        }
    }

    // 로깅 제외 경로 및 확장자 확인
    private static bool ShouldSkip(string path)
    {
        foreach (var prefix in ExcludePrefixes)
        {
            if (path.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }

        foreach (var ext in ExcludeExtensions)
        {
            if (path.EndsWith(ext, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }

        return false;
    }

    // 경로에 해당하는 메뉴 이름 찾기
    private static string? ResolveMenuName(string path)
    {
        var menus = Menus.GetMenus();

        return FindMenuName(menus, path.TrimEnd('/'));
    }

    // 재귀적으로 메뉴 트리 탐색
    private static string? FindMenuName(List<Menu> menus, string path)
    {
        foreach (var menu in menus)
        {
            if (!string.IsNullOrEmpty(menu.Path))
            {
                var menuPath = menu.Path.TrimEnd('/');
                if (path.Equals(menuPath, StringComparison.OrdinalIgnoreCase) || path.StartsWith(menuPath + "/", StringComparison.OrdinalIgnoreCase))
                {
                    return menu.Name;
                }
            }

            if (menu.Children is { Count: > 0 })
            {
                var childResult = FindMenuName(menu.Children, path);
                if (childResult is not null)
                {
                    return childResult;
                }
            }
        }

        return null;
    }
}