using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using XYY.Common.Standard;
using XYY.Core.Standard.Data.Infrastructure;
using XYY.Model.Standard;
using XYY.Service.Standard.UserService;

namespace XYY.Authentication.Standard
{
    public static class BasicAuthenticationScheme
    {
        public const string DefaultScheme = "Basic";
    }

    public class BasicAuthenticationOption : AuthenticationSchemeOptions
    {
    }


    /// <summary>
    /// API服务中间证
    /// 认证token
    /// </summary>
    public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOption>
    {

        private readonly BasicAuthenticationOption authOptions;
        private readonly IUserService userService;
        private readonly IUnitOfWork unitOfWork;
        public BasicAuthenticationHandler(
          IOptionsMonitor<BasicAuthenticationOption> options,
          ILoggerFactory logger,
          UrlEncoder encoder,
          ISystemClock clock,
          IUserService userService,
          IUnitOfWork unitOfWork)
          : base(options, logger, encoder, clock)
        {
            authOptions = options.CurrentValue;
            this.userService = userService;
            this.unitOfWork = unitOfWork;
        }

        public Endpoint GetEndpoint(HttpContext context)
        {
            if (context == null)
            {
                return null;
            }
            return context.Features.Get<IEndpointFeature>()?.Endpoint;
        }
        /// <summary>
        /// 认证 Token
        /// </summary>
        /// <returns></returns>
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            bool isAllow = false;
            var endpoint = GetEndpoint(this.Context);
            if (endpoint != null)
            {
                var allow = endpoint.Metadata.GetMetadata<IAllowAnonymous>();
                if (allow != null)
                {
                    isAllow = true;
                }
            }
            else
            {
                var claims = new[]
                   {
                        new Claim(ClaimTypes.StreetAddress,""),
                     };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
                unitOfWork.CurrentName = "";
                return await Task.FromResult(AuthenticateResult.Success(ticket)); 
            }


            if (isAllow || Request.Path.Value.Contains("dingtalk") || Request.Path.Value.Contains("SendNowCustomerQuotaion", StringComparison.InvariantCultureIgnoreCase))
            {
                var claims = new[]
                   {
                        new Claim(ClaimTypes.StreetAddress,""),
                     };
                var identity = new ClaimsIdentity(claims, Scheme.Name);
                var principal = new ClaimsPrincipal(identity);
                var ticket = new AuthenticationTicket(principal, Scheme.Name);
                unitOfWork.CurrentName = "";
                return await Task.FromResult(AuthenticateResult.Success(ticket));
            }
            if (!Request.Headers.ContainsKey("Authorization"))
            {
                if (string.IsNullOrEmpty(Request.Query["access_token"]))
                {
                    return AuthenticateResult.Fail("Missing Authorization ");
                }

            }
            try
            {
                string token = "";
                //var uservice = Context.RequestServices.GetService<IUserService>();
                if (Request.Headers.ContainsKey("Authorization"))
                {
                    var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
                    //var credentialBytes = Convert.FromBase64String(authHeader.Parameter);
                    if (!authHeader.Scheme.Equals("token", StringComparison.InvariantCultureIgnoreCase)
                        && !authHeader.Scheme.Equals("bearer", StringComparison.InvariantCultureIgnoreCase))
                    {
                        return AuthenticateResult.Fail("Invalid Token");
                    }
                    else
                    {
                        token = authHeader.Parameter;
                    }
                }
                else
                {
                    token = Request.Query["access_token"];
                }
                var user = await Authorized(token);
                if (user != null)
                {
                    ///当前用户的身份信息
                    var claims = new[]
                    {
                        new Claim(ClaimTypes.StreetAddress,token),
                       new Claim(ClaimTypes.NameIdentifier,user.Id.ToString()),
                       new Claim(ClaimTypes.Name,user.NiceName),
                       new Claim(ClaimTypes.GroupSid,user.CustomerId.ToString()),
                       new Claim(ClaimTypes.Role,string.Join(",",user.Roles)),
                       new Claim(ClaimTypes.AuthorizationDecision,string.Join(",",user.PrivilegeUrl))
                     };
                    var identity = new ClaimsIdentity(claims, Scheme.Name);
                    var principal = new ClaimsPrincipal(identity);
                    var ticket = new AuthenticationTicket(principal, Scheme.Name);
                    unitOfWork.CurrentName = user.NiceName;
                    return await Task.FromResult(AuthenticateResult.Success(ticket));
                }
                else
                {
                    return AuthenticateResult.Fail("token无效");
                }
            }
            catch
            {
                return AuthenticateResult.Fail("Invalid Authorization Header");
            }

        }

        /// <summary>
        /// 质询
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
        {
            //Response.Headers["WWW-Authenticate"] = $"Basic realm=our site";
            await base.HandleChallengeAsync(properties);
        }

        /// <summary>
        /// 认证失败
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
        {
            await base.HandleForbiddenAsync(properties);
        }

        private async Task<UserContent> Authorized(string token)
        {
            var ui = await userService.GetUserContentAtApi(token);
            return ui;
        }

    }


    public static class BasicAuthentication
    {
        public static void UseBasicAuthentication(this IApplicationBuilder app)
        {
            app.UseMiddleware<BasicAuthenticationMiddleware>();
        }

    }

    public class BasicAuthenticationMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public BasicAuthenticationMiddleware(RequestDelegate next, ILoggerFactory LoggerFactory)
        {
            _next = next;
            _logger = LoggerFactory.CreateLogger<BasicAuthenticationMiddleware>();
        }
        public async Task Invoke(HttpContext httpContext, IAuthenticationService authenticationService)
        {
            var authenticated = await authenticationService.AuthenticateAsync(httpContext, BasicAuthenticationScheme.DefaultScheme);
            //authenticated.Principal.Claims.
            //_logger.LogInformation("Access Status：" + authenticated.Succeeded);
            if (!authenticated.Succeeded)
            {
                await authenticationService.ForbidAsync(httpContext, BasicAuthenticationScheme.DefaultScheme, new AuthenticationProperties { });
                return;
            }
            await _next(httpContext);
        }
    }
}
