using MarketData.DataAccess;
using MarketData.MarketDataModel;
using MarketData.Utils;
using MarketData.Generator;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MarketData.DividendRiskParity
{
  public class DividendRiskParityGenerator
  {
    public static DividendRiskParityResult GenerateDividendRiskParity(Price currentPrice, double aum, DividendRiskParityPositionCollection dividendRiskParityCollection)
    {
      DateGenerator dateGenerator = new DateGenerator();
      DividendRiskParityResult dividendRiskParityResult = new DividendRiskParityResult();
      if (null == dividendRiskParityCollection || 0 == dividendRiskParityCollection.Count) return null;
      CalculateWeightsAndExposures(currentPrice, dividendRiskParityCollection);
      CalculateDividendYield(dividendRiskParityCollection,currentPrice);

      double totalExposure = dividendRiskParityCollection.Sum(x => x.Exposure);
      double totalWeightedDividendPercent = dividendRiskParityCollection.Sum(x => x.WeightedDividendPercent);
      double totalGainLoss = dividendRiskParityCollection.Sum(x => x.GainLoss);

      dividendRiskParityResult.Symbol = dividendRiskParityCollection.Take(1).FirstOrDefault().Symbol;
      dividendRiskParityResult.CostBasis = totalExposure / dividendRiskParityCollection.Sum(x => x.Shares);
      dividendRiskParityResult.Aum = aum;
      dividendRiskParityResult.AllocationPercent = ((totalExposure) / aum) * 100.00;
      dividendRiskParityResult.AnnualDividend = totalExposure * (totalWeightedDividendPercent / 100.00);
      if (totalGainLoss < 0.00) dividendRiskParityResult.DividendYearsToParity = Math.Abs(totalGainLoss) / dividendRiskParityResult.AnnualDividend;
      else dividendRiskParityResult.DividendYearsToParity = double.NaN;
      dividendRiskParityResult.DaysSinceLastTraded = Math.Abs(dateGenerator.DaysBetweenActual(dividendRiskParityCollection.Where(x => !x.TypeOfPosition.Equals(DividendRiskParityPosition.PositionType.Proforma)).Max(x => x.TradeDate), currentPrice.Date));
      dividendRiskParityResult.CurrentPrice = currentPrice;
      dividendRiskParityResult.CurrentDividendPercent = DividendHistoryGenerator.GetDividendYield(dividendRiskParityResult.Symbol, currentPrice) * 100.00;
      DividendRiskParityPosition proformaPosition = dividendRiskParityCollection.Where(x => x.TypeOfPosition.Equals(DividendRiskParityPosition.PositionType.Proforma)).FirstOrDefault();
      if (null != proformaPosition) dividendRiskParityResult.AdditionalDividendPerYer = (dividendRiskParityResult.CurrentDividendPercent / 100) * proformaPosition.Exposure;
      dividendRiskParityResult.ParityPrice = EquilibriumGenerator.GenerateEquilibriumPrice(dividendRiskParityCollection, currentPrice);
      dividendRiskParityResult.ParityPercent = ((currentPrice.Close - dividendRiskParityResult.ParityPrice.Close) / dividendRiskParityResult.ParityPrice.Close) * 100.00;
      dividendRiskParityResult.YieldPercent=(dividendRiskParityResult.AnnualDividend/totalExposure)*100.00;
      return dividendRiskParityResult;
    }
    private static void CalculateWeightsAndExposures(Price currentPrice, DividendRiskParityPositionCollection dividendRiskParityCollection)
    {
      foreach (DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection)
      {
        dividendRiskParityPosition.CurrentPrice=currentPrice.Close;
        dividendRiskParityPosition.Exposure = dividendRiskParityPosition.PurchasePrice * dividendRiskParityPosition.Shares;
        dividendRiskParityPosition.GainLoss = (dividendRiskParityPosition.Shares * currentPrice.Close) - dividendRiskParityPosition.Exposure;
        dividendRiskParityPosition.GainLossPercent=(((dividendRiskParityPosition.Shares * currentPrice.Close)-dividendRiskParityPosition.Exposure)/dividendRiskParityPosition.Exposure);
      }
      double totalExposure = dividendRiskParityCollection.Sum(x => x.Exposure);
      foreach (DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection)
      {
        dividendRiskParityPosition.Weight = dividendRiskParityPosition.Exposure / totalExposure;
      }
    }
    private static void CalculateDividendYield(DividendRiskParityPositionCollection dividendRiskParityCollection,Price currentPrice)
    {
      double totalExposure = dividendRiskParityCollection.Sum(x => x.Exposure);
      double weightAdjustedYield = double.NaN;
      foreach (DividendRiskParityPosition dividendRiskParityPosition in dividendRiskParityCollection)
      {
        Price price = new Price();
        price.Close = dividendRiskParityPosition.PurchasePrice;
        price.Date = DateTime.Now;
        double dividendYield=DividendHistoryGenerator.GetDividendYield(dividendRiskParityPosition.Symbol,price);
        if (double.IsNaN(dividendYield)) continue;
        if (double.IsNaN(weightAdjustedYield)) weightAdjustedYield = 0.00;
        dividendRiskParityPosition.WeightedDividendPercent = (dividendYield * ((dividendRiskParityPosition.Exposure / totalExposure)))*100.00;
        dividendRiskParityPosition.DividendPayment = dividendYield * dividendRiskParityPosition.Exposure;
      }
    }
  }
}
