using BukiVedi.Shared;
using BukiVedi.Shared.Models;
using BukiVedi.Shared.Services;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Net;
using ZeroLevel;
using ZeroLevel.Services.Serialization;

namespace BukiVedi.App.Middlewares
{
    public static class BukiVediAuthMiddlewareExtension
    {
        public static IApplicationBuilder UseBukiVediAuthMiddleware(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<BukiVediAuthMiddleware>();
        }
    }

    public class BukiVediAuthMiddleware
    {
        private readonly IAuthProvider _authProvider;
        private readonly RequestDelegate _next;

        public BukiVediAuthMiddleware(RequestDelegate next, IAuthProvider authProvider)
        {
            this._authProvider = authProvider;
            this._next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            await ReadDataFromContext(context);
            try
            {
                await _next(context);
            }
            catch (Exception ex)
            {
                var controllerActionDescriptor = context?
                .GetEndpoint()?
                .Metadata?
                .GetMetadata<ControllerActionDescriptor>();
                var controllerName = controllerActionDescriptor?.ControllerName ?? string.Empty;
                var actionName = controllerActionDescriptor?.ActionName ?? string.Empty;
                Log.Error(ex, $"[{controllerName}.{actionName}]");
                if (context != null)
                {
                    switch (ex)
                    {
                        case KeyNotFoundException
                            or FileNotFoundException:
                            context.Response.StatusCode = (int)HttpStatusCode.NotFound;
                            break;

                        case UnauthorizedAccessException:
                            context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
                            break;

                        default:
                            context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                            break;
                    }
                    await context.Response.WriteAsync(ex.Message ?? "Error");
                }
            }
        }

        private async Task ReadDataFromContext(HttpContext context)
        {
            var op_context = new OperationContext(_authProvider, Timestamp.UtcNow);
            string token = context.User?.Claims?.FirstOrDefault(c => c.Type.Equals("Token"))?.Value!;
            if (string.IsNullOrWhiteSpace(token) == false)
            {
                // TRY AUTHORIZE
                var authData = ReadAccountInfoFromToken(token);
                if (authData != null)
                {
                    var account = await _authProvider.GetAccountByLogin(authData.Login);
                    await op_context.SetAccount(account);                    
                    context.Items["op_context"] = op_context;
                    return;
                }
                else
                {
                    context.Items["op_context"] = op_context;
                    return;
                }
            }
            context.Items["op_context"] = op_context;
            return;
        }

        internal static AuthTokenData ReadAccountInfoFromToken(string token)
        {
            if (string.IsNullOrWhiteSpace(token) == false && string.CompareOrdinal(token, "null") != 0)
            {
                var data = Convert.FromBase64String(token);
                var decrypted = Cryptor.Decrypt(data);
                var ai = MessageSerializer.Deserialize<AuthTokenData>(decrypted);
                if (string.IsNullOrWhiteSpace(ai.Login) == false)
                {
                    return ai;
                }
            }
            return null!;
        }
    }
}
