using System;
using System.Collections.Generic;

namespace NT8.Core.Intelligence
{
    /// <summary>
    /// Filters trades by grade according to active risk mode and returns size multipliers.
    /// </summary>
    public class GradeFilter
    {
        private readonly object _lock = new object();
        private readonly Dictionary<RiskMode, TradeGrade> _minimumGradeByMode;
        private readonly Dictionary<RiskMode, Dictionary<TradeGrade, double>> _sizeMultipliers;

        /// <summary>
        /// Creates a new grade filter with default mode rules.
        /// </summary>
        public GradeFilter()
        {
            _minimumGradeByMode = new Dictionary<RiskMode, TradeGrade>();
            _sizeMultipliers = new Dictionary<RiskMode, Dictionary<TradeGrade, double>>();

            InitializeDefaults();
        }

        /// <summary>
        /// Returns true when a trade with given grade should be accepted for the risk mode.
        /// </summary>
        /// <param name="grade">Trade grade.</param>
        /// <param name="mode">Current risk mode.</param>
        /// <returns>True when accepted, false when rejected.</returns>
        public bool ShouldAcceptTrade(TradeGrade grade, RiskMode mode)
        {
            lock (_lock)
            {
                if (!_minimumGradeByMode.ContainsKey(mode))
                    return false;

                if (mode == RiskMode.HR)
                    return false;

                var minimum = _minimumGradeByMode[mode];
                return (int)grade >= (int)minimum;
            }
        }

        /// <summary>
        /// Returns size multiplier for the trade grade and risk mode.
        /// </summary>
        /// <param name="grade">Trade grade.</param>
        /// <param name="mode">Current risk mode.</param>
        /// <returns>Size multiplier. Returns 0.0 when trade is rejected.</returns>
        public double GetSizeMultiplier(TradeGrade grade, RiskMode mode)
        {
            lock (_lock)
            {
                if (!ShouldAcceptTrade(grade, mode))
                    return 0.0;

                if (!_sizeMultipliers.ContainsKey(mode))
                    return 0.0;

                var map = _sizeMultipliers[mode];
                if (map.ContainsKey(grade))
                    return map[grade];

                return 0.0;
            }
        }

        /// <summary>
        /// Returns human-readable rejection reason for grade and risk mode.
        /// </summary>
        /// <param name="grade">Trade grade.</param>
        /// <param name="mode">Current risk mode.</param>
        /// <returns>Rejection reason or empty string when accepted.</returns>
        public string GetRejectionReason(TradeGrade grade, RiskMode mode)
        {
            lock (_lock)
            {
                if (mode == RiskMode.HR)
                    return "Risk mode HR blocks all new trades";

                if (!ShouldAcceptTrade(grade, mode))
                {
                    var min = GetMinimumGrade(mode);
                    return string.Format("Grade {0} below minimum {1} for mode {2}", grade, min, mode);
                }

                return string.Empty;
            }
        }

        /// <summary>
        /// Gets minimum grade required for a risk mode.
        /// </summary>
        /// <param name="mode">Current risk mode.</param>
        /// <returns>Minimum accepted trade grade.</returns>
        public TradeGrade GetMinimumGrade(RiskMode mode)
        {
            lock (_lock)
            {
                if (_minimumGradeByMode.ContainsKey(mode))
                    return _minimumGradeByMode[mode];

                return TradeGrade.APlus;
            }
        }

        private void InitializeDefaults()
        {
            _minimumGradeByMode.Add(RiskMode.ECP, TradeGrade.B);
            _minimumGradeByMode.Add(RiskMode.PCP, TradeGrade.C);
            _minimumGradeByMode.Add(RiskMode.DCP, TradeGrade.A);
            _minimumGradeByMode.Add(RiskMode.HR, TradeGrade.APlus);

            var ecp = new Dictionary<TradeGrade, double>();
            ecp.Add(TradeGrade.APlus, 1.5);
            ecp.Add(TradeGrade.A, 1.25);
            ecp.Add(TradeGrade.B, 1.0);
            _sizeMultipliers.Add(RiskMode.ECP, ecp);

            var pcp = new Dictionary<TradeGrade, double>();
            pcp.Add(TradeGrade.APlus, 1.25);
            pcp.Add(TradeGrade.A, 1.1);
            pcp.Add(TradeGrade.B, 1.0);
            pcp.Add(TradeGrade.C, 0.9);
            _sizeMultipliers.Add(RiskMode.PCP, pcp);

            var dcp = new Dictionary<TradeGrade, double>();
            dcp.Add(TradeGrade.APlus, 0.75);
            dcp.Add(TradeGrade.A, 0.5);
            _sizeMultipliers.Add(RiskMode.DCP, dcp);

            _sizeMultipliers.Add(RiskMode.HR, new Dictionary<TradeGrade, double>());
        }
    }
}
