using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using LDX.Core;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using LDX.BaseData.Domain.IServices;
using LDX.BaseData.Domain.Entities;
using LDX.MES.Domain.IServices.FM;

namespace LDX.WebApi.Providers
{
    /// <summary>
    /// OAuth Provider
    /// </summary>
    public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
    {
        #region fields

        /// <summary>
        /// 管理后台ClientId
        /// </summary>
        const string AdminPortalClientId = "witbackstage";
        const string AdminPlatformId = "witplatform";
        const string AppClientId = "witjitmesapp";
        string clientId;
        /// <summary>
        /// 公钥
        /// </summary>
        static string _PublicKey;

        /// <summary>
        /// 允许的clientId 列表
        /// </summary>
        static IList<string> _ClientIdList;
        #endregion

        #region ctor.
        public ApplicationOAuthProvider()
        {
        }

        static ApplicationOAuthProvider()
        {
            _PublicKey = "LDX2017#";
            _ClientIdList = new List<string>();
            for (int i = 1; i <= 40; i++)
            {
                _ClientIdList.Add(string.Concat("wt", i.PadLeft(3, '0')));
            }
        }
        #endregion

        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "password". This occurs when the user has provided name and password
        /// credentials directly into the client application's user interface, and the client application is using those to acquire an "access_token" and
        /// optional "refresh_token". If the web application supports the
        /// resource owner credentials grant type it must validate the context.Username and context.Password as appropriate. To issue an
        /// access token the context.Validated must be called with a new ticket containing the claims about the resource owner which should be associated
        /// with the access token. The application should take appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
        /// The default behavior is to reject this grant type.
        /// See also http://tools.ietf.org/html/rfc6749#section-4.3.2
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>
        /// Task to enable asynchronous execution
        /// </returns>
        public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {


            //Tuple<string, string> departmentInfo;
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            try
            {
                /*
                var userService = ServiceLocator.Instance.GetService<IUserService>();
                loginUser = userService.Login(context.UserName, context.Password);
                if (loginUser == null)
                {
                    throw new DomainException("用户名或密码不正确。");
                }
                if (IsFromAdminPortal(context) && !loginUser.CanLoginAdminPortal)
                {
                    throw new DomainException("用户没有登录管理后台的权限。");
                }
                if (IsFromAdminPortal(context) && !loginUser.CanLoginAdminPortal)
                {
                    throw new DomainException("用户没有登录管理后台的权限。");
                }
                if (IsFromPos(context) && !loginUser.CanLoginPos)
                {
                    throw new DomainException("用户没有登录Pos的权限。");
                } */

                //departmentInfo = GetDepartmentInfo(loginUser);
                //if (clientId == AdminPortalClientId || clientId == AppClientId)
                //{
                    var userService = ServiceLocator.Instance.GetService<IFMUserService>();
                    var loginUser = userService.Get(i => (i.CODE.ToUpper() == context.UserName.ToUpper()
                        || i.PHONE == context.UserName) && i.PASSWORD.ToUpper() == context.Password.ToUpper());
                    if (loginUser == null)
                    {
                        context.SetError("invalid_grant", "用户不存在或密码错误");
                        return;
                    }
                    else
                    {

                        oAuthIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, loginUser.ID.ToString()));
                        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, loginUser.NAME));
                        //oAuthIdentity.AddClaim(new Claim(ClaimTypes.Role, loginUser.ROLE));
                        //oAuthIdentity.AddClaim(new Claim(ClaimTypes.Uri, context.Request.Uri.AbsoluteUri));
                        oAuthIdentity.AddClaim(new Claim(LDX.Core.Security.ClaimTypes.UserCode, loginUser.CODE));
						if (loginUser.ORG_ID != null)
							oAuthIdentity.AddClaim(new Claim(LDX.Core.Security.ClaimTypes.OrgId, loginUser.ORG_ID.ToString()));
                        //oAuthIdentity.AddClaim(new Claim(LDX.Core.Security.ClaimTypes.DepartmentId, loginUser.DEPARTMENT_ID == null ? "" : loginUser.DEPARTMENT_ID.ToString()));
                    }
    //            }
    //            else
    //            {
				//	context.SetError("invalid_grant", "用户不存在或密码错误");
				//	return;
				//	//var platformUserService = ServiceLocator.Instance.GetService<IPlatformUserService>();
				//	//var platUser = platformUserService.Get(i => i.LOGIN_NAME.ToUpper() == context.UserName.ToUpper() && i.PASSWORD == context.Password.ToUpper());
				//	//if (platUser == null)
				//	//{
				//	//    context.SetError("invalid_grant", "用户不存在或密码错误");
				//	//    return;
				//	//}
				//	//else
				//	//{
				//	//    oAuthIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, platUser.ID.ToString()));
				//	//    //oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, platUser.NAME));
				//	//    oAuthIdentity.AddClaim(new Claim(LDX.Core.Security.ClaimTypes.UserCode, platUser.LOGIN_NAME));
				//	//    oAuthIdentity.AddClaim(new Claim(LDX.Core.Security.ClaimTypes.Cid, platUser.CID));
				//	//}
				//}

			}
            catch (DomainException domainEx)
            {
                context.SetError("invalid_grant", domainEx.Message);
                return;
            }
            catch (Exception ex)
            {
                context.SetError("invalid_grant", ex.Message);
                return;
            }

            var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
            context.Validated(ticket);
            await base.GrantResourceOwnerCredentials(context);
        }

        private bool IsFromAdminPortal(OAuthGrantResourceOwnerCredentialsContext context)
        {
            return AdminPortalClientId.Equals(context.ClientId);
        }

        private bool IsFromPos(OAuthGrantResourceOwnerCredentialsContext context)
        {
            return !IsFromAdminPortal(context);
        }
        //private Tuple<string, string> GetDepartmentInfo(BaseData.Domain.Entities.User loginUser)
        //{
        //    var departmentCode = System.Configuration.ConfigurationManager.AppSettings["DepartmentCode"];
        //    var departmentService = ServiceLocator.Instance.GetService<IDepartmentService>();
        //    var department = string.IsNullOrWhiteSpace(departmentCode) ? departmentService.Get(loginUser.DepartmentID) : departmentService.Query(p => p.Code == departmentCode).FirstOrDefault();
        //    if (department == null)
        //        throw new DomainException("服务端异常，未找到配置的门店信息。");
        //    return new Tuple<string, string>(department.ID.ToString(), department.Name);
        //}

        /// <summary>
        /// Called when a request to the Token endpoint arrives with a "grant_type" of "client_credentials". This occurs when a registered client
        /// application wishes to acquire an "access_token" to interact with protected resources on it's own behalf, rather than on behalf of an authenticated user.
        /// If the web application supports the client credentials it may assume the context.ClientId has been validated by the ValidateClientAuthentication call.
        /// To issue an access token the context.Validated must be called with a new ticket containing the claims about the client application which should be associated
        /// with the access token. The application should take appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
        /// The default behavior is to reject this grant type.
        /// See also http://tools.ietf.org/html/rfc6749#section-4.4.2
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>
        /// Task to enable asynchronous execution
        /// </returns>
        public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
        {
            var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
            oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.ClientId));
            var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
            context.Validated(ticket);

            return base.GrantClientCredentials(context);
        }

        /// <summary>
        /// Called to validate that the origin of the request is a registered "client_id", and that the correct credentials for that client are
        /// present on the request. If the web application accepts Basic authentication credentials,
        /// context.TryGetBasicCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request header. If the web
        /// application accepts "client_id" and "client_secret" as form encoded POST parameters,
        /// context.TryGetFormCredentials(out clientId, out clientSecret) may be called to acquire those values if present in the request body.
        /// If context.Validated is not called the request will not proceed further.
        /// </summary>
        /// <param name="context">The context of the event carries information in and results out.</param>
        /// <returns>
        /// Task to enable asynchronous execution
        /// </returns>
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {

            string clientSecret;
            context.TryGetBasicCredentials(out clientId, out clientSecret);
            //if (AdminPortalClientId.Equals(clientId) || AppClientId.Equals(clientId) || AdminPlatformId.Equals(clientId))//管理后台，授权通过
            //{
                context.Validated(clientId);
            //}
            //else if (_ClientIdList.Contains(clientId) && _PublicKey.Equals(clientSecret))//校验POS前端私钥和公钥
            //{
            //    context.Validated(clientId);
            //}

            return base.ValidateClientAuthentication(context);
        }
    }
}