using Binance.TradeRobot.Business.Extensions;
using Binance.TradeRobot.Common.DI;
using Binance.TradeRobot.Common.Extensions;
using Binance.TradeRobot.Model.Base;
using Binance.TradeRobot.Model.Db;
using Binance.TradeRobot.Model.Dto;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using Yitter.IdGenerator;

namespace Binance.TradeRobot.Business
{
    [BatchRegistration(ServiceLifetime.Singleton, RegistrationType.Self)]
    public class UserBusiness : BaseBusiness
    {
        private IConfiguration configuration;

        public UserBusiness(IFreeSql fsql, NLogManager logManager, IIdGenerator idGenerator, IConfiguration configuration, IMemoryCache memoryCache) : base(fsql, logManager, idGenerator, memoryCache)
        {
            this.configuration = configuration;
        }

        public LoginResponse Login(LoginRequest loginRequest)
        {
            var pwd = loginRequest.Pwd.ToMD5();
            var user = fsql.Select<User>().Where(u => u.UserName == loginRequest.UserName && u.Pwd == pwd).ToOne();
            if (user == null)
                throw new BusinessException("用户名或密码错误");

            #region 签发token
            var claims = new List<Claim>()
            {
                new Claim("Id",user.Id.ToString()),
                new Claim("UserName",user.UserName),
            };

            var jwtSecret = configuration.GetSection("JwtConfig:Secret").Value;
            var jwtIssuer = configuration.GetSection("JwtConfig:Issuer").Value;
            var jwtAudience = configuration.GetSection("JwtConfig:Audience").Value;
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret));
            var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwtToken = new JwtSecurityToken(jwtIssuer, jwtAudience, claims, expires: DateTime.Now.AddDays(30), signingCredentials: credentials);
            var token = new JwtSecurityTokenHandler().WriteToken(jwtToken);
            #endregion
            return new LoginResponse()
            {
                Id = user.Id,
                UserName = user.UserName,
                Token = token
            };
        }

        public IList<UserResponse> GetUserList()
        {
            var userList = fsql.Select<User>().ToList(u => new UserResponse()
            {
                Id = u.Id,
                CostAmount = u.CostAmount,
                CreateTime = u.CreateTime,
                Profit = u.Profit,
                UpdateTime = u.UpdateTime,
                UserName = u.UserName,
                WithdrawAmount = u.WithdrawAmount
            });
            userList.CalculateRatio();
            return userList;
        }

        #region 用户资产

        /// <summary>
        /// 单向资产变更
        /// </summary>
        /// <param name="operationUserId"></param>
        /// <param name="changeAmount"></param>
        /// <param name="changeUserId"></param>
        /// <param name="capitalChangeType"></param>
        /// <param name="remark"></param>
        /// <exception cref="BusinessException"></exception>
        private void OneWayAssetChange(long operationUserId, decimal changeAmount, long changeUserId, Enums.CapitalChangeType capitalChangeType, string remark)
        {
            var changeUser = fsql.Select<User>(changeUserId).ToOne().Map<UserResponse>();
            if (changeUser == null)
                throw new BusinessException("资金变更用户不存在");

            if (capitalChangeType == Enums.CapitalChangeType.Reduce)
            {
                if (changeUser.TotalAssets < changeAmount)
                    throw new BusinessException("用户资产小于减持资金,不能提现");

                //获取交易所账户总额(usdt)
                //var apiKeyList = stockExchangeAccountBusiness.GetAPIKeyList(null);
                //var totalBalance = apiKeyList.Sum(k => k.Balance);
                //if (totalBalance < changeAmount)
                //    throw new BusinessException("交易所总余额小于减持资金,不能提现");
            }

            if (capitalChangeType == Enums.CapitalChangeType.Add)
            {
                changeUser.CostAmount += changeAmount;
            }
            else if (capitalChangeType == Enums.CapitalChangeType.Reduce)
            {
                if (changeUser.Profit > 0)
                {
                    if (changeUser.Profit >= changeAmount)
                        changeUser.Profit -= changeAmount;  //收益足够提现,只扣收益
                    else
                    {
                        var lessChangeAmount = changeAmount;  //收益不足提现,先扣收益,不足部分再扣本金
                        lessChangeAmount -= changeUser.Profit;
                        changeUser.Profit = 0;
                        changeUser.CostAmount -= lessChangeAmount;
                    }
                }
                else
                {
                    changeUser.CostAmount -= changeAmount;
                }
            }

            fsql.Transaction(() =>
            {
                fsql.Update<User>(changeUser.Id).Set(u => u.CostAmount, changeUser.CostAmount)
                                                         .Set(u => u.Profit, changeUser.Profit)
                                                         .SetIf(capitalChangeType == Enums.CapitalChangeType.Reduce, u => u.WithdrawAmount + changeAmount)
                                                         .ExecuteAffrows();

                fsql.Insert(new UserAccountFundChangeRecord()
                {
                    Id = idGenerator.NewLong(),
                    ChangeAmount = changeAmount,
                    UserId = changeUserId,
                    OperationType = capitalChangeType,
                    OperationUserId = operationUserId,
                    Remark = remark,
                    Direction = capitalChangeType == Enums.CapitalChangeType.Add ? Enums.FundDirection.In : Enums.FundDirection.Out
                }).ExecuteAffrows();
            });
        }

        public void AddFunds(long operationUserId, FundsChangeRequest fundsChangeRequest)
        {
            if (fundsChangeRequest.ChangeAmount <= 0)
                throw new BusinessException("无效金额");
            OneWayAssetChange(operationUserId, fundsChangeRequest.ChangeAmount, fundsChangeRequest.UserId, Enums.CapitalChangeType.Add, fundsChangeRequest.Remark);
        }

        public void ReduceFunds(long operationUserId, FundsChangeRequest fundsChangeRequest)
        {
            if (fundsChangeRequest.ChangeAmount <= 0)
                throw new BusinessException("无效金额");
            OneWayAssetChange(operationUserId, fundsChangeRequest.ChangeAmount, fundsChangeRequest.UserId, Enums.CapitalChangeType.Reduce, fundsChangeRequest.Remark);
        }

        public void TransferFunds(long operationUserId, FundsTransferRequest fundsTransferRequest)
        {
            if (fundsTransferRequest.ChangeAmount <= 0)
                throw new BusinessException("无效金额");

            var userList = fsql.Select<User>().ToList().Map<IList<UserResponse>>();
            var fromUser = userList.FirstOrDefault(u => u.Id == fundsTransferRequest.FromUserId);
            if (fromUser == null)
                throw new BusinessException("资金变更用户不存在");
            var toUser = userList.FirstOrDefault(u => u.Id == fundsTransferRequest.ToUserId);
            if (toUser == null)
                throw new BusinessException("资金变更用户不存在");

            if (fromUser.TotalAssets < fundsTransferRequest.ChangeAmount)
                throw new BusinessException("用户资产小于转移资金,不能转移");

            if (fromUser.Profit > 0)
            {
                if (fromUser.Profit >= fundsTransferRequest.ChangeAmount)
                    fromUser.Profit -= fundsTransferRequest.ChangeAmount;  //收益足够提现,只扣收益
                else
                {
                    var lessChangeAmount = fundsTransferRequest.ChangeAmount;  //收益不足提现,先扣收益,不足部分再扣本金
                    lessChangeAmount -= fromUser.Profit;
                    fromUser.Profit = 0;
                    fromUser.CostAmount -= lessChangeAmount;
                }
            }
            else
            {
                fromUser.CostAmount -= fundsTransferRequest.ChangeAmount;
            }

            fsql.Transaction(() =>
            {
                fsql.Update<User>(fromUser.Id).Set(u => u.CostAmount, fromUser.CostAmount)
                                                       .Set(u => u.Profit, fromUser.Profit)
                                                       .Set(u => u.WithdrawAmount + fundsTransferRequest.ChangeAmount)
                                                       .ExecuteAffrows();

                fsql.Update<User>(toUser.Id).Set(u => u.CostAmount + fundsTransferRequest.ChangeAmount)
                                                     .ExecuteAffrows();

                fsql.Insert(new UserAccountFundChangeRecord()
                {
                    Id = idGenerator.NewLong(),
                    ChangeAmount = fundsTransferRequest.ChangeAmount,
                    UserId = fundsTransferRequest.FromUserId,
                    OperationType = Enums.CapitalChangeType.Transfer,
                    OperationUserId = operationUserId,
                    Remark = fundsTransferRequest.Remark,
                    Direction = Enums.FundDirection.Out,
                    ToUserId = fundsTransferRequest.ToUserId
                }).ExecuteAffrows();

                fsql.Insert(new UserAccountFundChangeRecord()
                {
                    Id = idGenerator.NewLong(),
                    ChangeAmount = fundsTransferRequest.ChangeAmount,
                    UserId = fundsTransferRequest.ToUserId,
                    OperationType = Enums.CapitalChangeType.Transfer,
                    OperationUserId = operationUserId,
                    Remark = fundsTransferRequest.Remark,
                    Direction = Enums.FundDirection.In,
                    ToUserId = fundsTransferRequest.FromUserId
                }).ExecuteAffrows();
            });
        }

        public UserAccountChangeRecordListResponse GetUserAccountFundChangeRecordList(QueryUserAccountRequest queryUserAccountRequest)
        {
            var list = fsql.Select<UserAccountFundChangeRecord, User, User, User>()
                           .InnerJoin((ucr, u, ou, tu) => ucr.UserId == u.Id)
                           .InnerJoin((ucr, u, ou, tu) => ucr.OperationUserId == ou.Id)
                           .LeftJoin((ucr, u, ou, tu) => ucr.ToUserId == tu.Id)
                           .Where((ucr, u, ou, tu) => ucr.UserId == queryUserAccountRequest.UserId)
                           .Count(out var recordCount)
                           .Page(queryUserAccountRequest.PageIndex, queryUserAccountRequest.PageSize)
                           .ToList((ucr, u, ou, tu) => new UserAccountFundChangeRecordResponse()
                           {
                               Id = ucr.Id,
                               ChangeAmount = ucr.ChangeAmount,
                               CreateTime = ucr.CreateTime,
                               OperationType = ucr.OperationType,
                               OperationUserId = ucr.OperationUserId,
                               OperationUserName = ou.UserName,
                               UserId = ucr.UserId,
                               UserName = u.UserName,
                               Remark = ucr.Remark,
                               Direction = ucr.Direction,
                               ToUserId = tu.Id,
                               ToUserName = tu.UserName
                           });
            return new UserAccountChangeRecordListResponse()
            {
                List = list,
                RecordCount = recordCount
            };
        }

        //public UserAccountProfitLossRecordListResponse GetUserAccountProfitLossRecordList(QueryUserAccountRequest queryUserAccountRequest)
        //{
        //    var list = fsql.Select<Model.Db.UserAccountProfitLossRecord, Model.Db.User, Model.Db.Robot>()
        //                   .WithLock(SqlServerLock.NoLock)
        //                   .InnerJoin((p, u, r) => p.UserId == u.Id)
        //                   .InnerJoin((p, u, r) => p.RobotId == r.Id)
        //                   .Where((p, u, r) => p.UserId == queryUserAccountRequest.UserId)
        //                   .Count(out var recordCount)
        //                   .Page(queryUserAccountRequest.PageIndex, queryUserAccountRequest.PageSize)
        //                   .ToList((p, u, r) => new UserAccountProfitLossRecordResponse()
        //                   {
        //                       Id = p.Id,
        //                       ChangeAmount = p.ChangeAmount,
        //                       CreateTime = p.CreateTime,
        //                       DividendRatio = p.DividendRatio,
        //                       OrderProfit = p.OrderProfit,
        //                       RobotId = p.RobotId,
        //                       SpotOrderId = p.SpotOrderId,
        //                       Symbol = r.Symbol,
        //                       UserId = p.UserId,
        //                       UserName = u.UserName,
        //                       UserProfit = p.UserProfit
        //                   });
        //    return new UserAccountProfitLossRecordListResponse()
        //    {
        //        List = list,
        //        RecordCount = recordCount
        //    };
        //}

        #endregion
    }
}