using e_suite.API.Common;
using e_suite.API.Common.exceptions;
using eSuite.API.security;
using eSuite.API.Utilities;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Autofac;

namespace eSuite.API.Middleware;

internal class SecurityAccessMiddleWare
{
    private readonly RequestDelegate _next;
    private readonly ILifetimeScope _lifetimeScope;

    public SecurityAccessMiddleWare(RequestDelegate next, ILifetimeScope lifetimeScope)
    {
        _next = next;
        _lifetimeScope = lifetimeScope;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var endpoint = context.GetEndpoint() ?? throw new NotFoundException("Endpoint not found");

        if (endpoint is RouteEndpoint routeEndpoint)
        {
            if (routeEndpoint.RoutePattern.RawText != null)
            {
                var allowAnonymousAttribute = endpoint?.Metadata.OfType<AllowAnonymousAttribute>().FirstOrDefault();

                var routePatternLower = routeEndpoint.RoutePattern.RawText.ToLower().TrimStart('/');
                if (allowAnonymousAttribute != null || !(routePatternLower.StartsWith("api") || routePatternLower.StartsWith("account")))
                {
                    await _next.Invoke(context);
                    return;
                }
            }
        }

        var accessKeyAttribute = endpoint?.Metadata.OfType<AccessKeyAttribute>().FirstOrDefault() ?? throw new NotImplementedException("Unable to find AccessKeyAttribute for Endpoint");
        
        var userId = GetUserIdFromContextUser(context.User).GetValueOrDefault();
        
        var accessKey = accessKeyAttribute.SecurityAccess;

        await using var requestLifetimeScope = _lifetimeScope.BeginLifetimeScope();
        var roleManager = requestLifetimeScope.Resolve<IRoleManager>();

            if (await roleManager.HasAnyAccess(userId, accessKey))
        {
            await _next.Invoke(context);
            return;
        }

        var problemDetails = new ProblemDetails
        {
            Title = "Unauthorised",
            Detail = "User does not have permission to call this method"
        };

        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        context.Response.ContentType = "application/json";

        await context.Response.WriteAsync(problemDetails.ToJson());
    }
    private static long? GetUserIdFromContextUser(ClaimsPrincipal user)
    {
        long? userId;
        try
        {
            userId = user.Id();
        }
        catch
        {
            userId = null;
        }

        return userId;
    }
}