using Knoks.Core.Data.Interfaces;
using Knoks.Core.Entities;
using Knoks.Core.Entities.Args;
using Knoks.Core.Logic.Interfaces;
using Microsoft.Extensions.Logging;
using System.Linq;
using System.Threading.Tasks;

namespace Knoks.Core.Logic.Managers
{
    public class KnokPriceManager : IKnokPricelManager
    {
        private readonly ILogger<KnokPriceManager> _logger;
        private readonly IUserDao _userDao;
        private readonly IKnokDao _knokDao;
        private readonly IKnokPriceDao _knokPriceDao;
        private readonly IKnokPriceLogic _knokPriceLogic;
        private readonly IKnokSettings _settings;

        //TODO - move this to settings
        private const decimal _objectiveWeight = 0.75m;
        private const decimal _analysisMultiplier = 0.33m;
        private const decimal _potenialProfitMultiplier = 10m;
        private const decimal _profitTimeRatioMultiplier = 100m;
        private const decimal _profitLossRatioMultiplier = 100m;

        public KnokPriceManager(ILogger<KnokPriceManager> logger, IKnokDao knokDao, IUserDao userDao, IKnokPriceLogic knokPriceLogic, IKnokPriceDao knokPriceDao, IKnokSettings settings)
        {
            _logger = logger;
            _userDao = userDao;
            _knokDao = knokDao;
            _knokPriceLogic = knokPriceLogic;
            _knokPriceDao = knokPriceDao;
            _settings = settings;
        }

        private async Task<decimal> GetExpectancy(long userId)
        {
            var probabilityOfWinning = await _knokPriceDao.GetProbabilityOfWinning(userId);
            var winPayoff = await _knokPriceDao.GetWinPayoff(userId);
            var lossPayoff = await _knokPriceDao.GetLossPayoff(userId);

            return _knokPriceLogic.CalculateExpectancy(probabilityOfWinning, winPayoff, lossPayoff);
        }

        private decimal GetPaidPercent(UserRank userRank)
        {
            var userRankValue = (UserRankValues)userRank;

            return ((int)userRankValue) / 100;
        }

        private async Task<decimal> GetObjectivePrice(long userId, UserRank userRank)
        {
            var expectancy = await GetExpectancy(userId);
            var paidPercent = GetPaidPercent(userRank);

            return _knokPriceLogic.CalculateObjectivePrice(expectancy, paidPercent, _objectiveWeight, _settings.Multiplier);
        }

        private decimal GetSubjectivePrice(CreateKnokArgs knok)
        {
            var faPrice = knok.Fundamental != null ?
                _knokPriceLogic.CalcAnalysisPrice(knok.Fundamental.Header, knok.Fundamental.Description, knok.Fundamental.Tags, _analysisMultiplier) : 0m;
            var taPrice = knok.Technical != null ?
                _knokPriceLogic.CalcAnalysisPrice(knok.Technical.Header, knok.Technical.Description, knok.Technical.Tags, _analysisMultiplier) : 0m;
            var potentialProfitPrice = _knokPriceLogic.CalculatePotentialProfitPrice(knok.PotentialProfitValue, _potenialProfitMultiplier);//a
            var profitTimeRatioPrice = _knokPriceLogic.CalculateProfitTimeRatioPrice(knok.PotentialProfitValue,knok.Duration,_profitTimeRatioMultiplier);//b
            var profitLossRatioPrice = _knokPriceLogic.CalculateProfitLossRatioPrice(knok.PotentialProfitValue, knok.EntryPriceFrom, knok.EntryPriceTo, knok.StopLoss, _profitLossRatioMultiplier);//c
            return _knokPriceLogic.CalculateSubjectivePrice(faPrice, taPrice, potentialProfitPrice, profitTimeRatioPrice, profitLossRatioPrice);

        }

        private decimal GetNetContribution(CreateKnokArgs knok)
        {
            var subjectivePrice = GetSubjectivePrice(knok);

            return subjectivePrice * (1 - _objectiveWeight);
        }

        public async Task<decimal> TotalPrice(CreateKnokArgs knok, long userId)
        {
            var users = await _userDao.GetUsers(userId: userId);
            var userTypeId = users.First().UserTypeId;
            var userRank = (UserRank)userTypeId;

            knok.PotentialProfitValue = _knokPriceLogic.CalculatePotentialProfit(knok.EntryPriceFrom, knok.EntryPriceTo, knok.ExitPriceFrom, knok.ExitPriceTo);

            var objectivePrice = await GetObjectivePrice(userId, userRank);
            var netContribution = GetNetContribution(knok);
            var rankPayoff = _knokPriceLogic.RankPayoff(userRank);
            var rankWeighted = rankPayoff / 100m * netContribution;
            return objectivePrice + rankWeighted;
        }
    }
}
