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

namespace MarketData.Generator
{
  public class RiskFreeRateGenerator
  {
    private RiskFreeRateGenerator()
    {
    }
    public static Price GeneratePriceAtRiskFreeRate(PortfolioTrades portfolioTrades)
    {
      Price price=new Price();
      double cumulativePortfolioInvestment=0;
      if(null==portfolioTrades||0==portfolioTrades.Count) return null;
      double totalShares=(from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum();
      List<int> yearsToProcess=GetYearsToProcess(portfolioTrades);
      foreach(int processYear in yearsToProcess)
      {
        cumulativePortfolioInvestment=GetInvestmentForYear(portfolioTrades,processYear,cumulativePortfolioInvestment);
        if(double.NaN.Equals(cumulativePortfolioInvestment)) return null;
      }
      price.Close=price.Open=price.High=price.Low=price.AdjClose=cumulativePortfolioInvestment/totalShares;
      price.Date=DateTime.Now;
      return price;
    }
    private static List<int> GetYearsToProcess(PortfolioTrades portfolioTrades)
    {
      List<int> investmentYears=new List<int>();
      int startingYear=portfolioTrades[0].TradeDate.Year;
      int endingYear=DateTime.Now.Year;

      for(int processYear=startingYear;processYear<=endingYear;processYear++)
      {
        investmentYears.Add(processYear);
      }
      return investmentYears;
    }
    private static double GetInvestmentForYear(PortfolioTrades portfolioTrades,int investmentYear,double initialInvestment)
    {
      double cumulativeYearInvestment=0;
      DateTime yearEnd=new DateTime(investmentYear,12,31);
      DateTime startOfYear=new DateTime(investmentYear,1,1);
      double riskFreeRate;
      PortfolioTrades tradesInYear=new PortfolioTrades((from PortfolioTrade portfolioTrade in portfolioTrades where portfolioTrade.TradeDate.Year.Equals(investmentYear) select portfolioTrade).ToList());

      if(0.00!=initialInvestment)
      {
        riskFreeRate=YieldCurveDA.GetRiskFreeRate1Yr(startOfYear,10);
        if(double.NaN.Equals(riskFreeRate)) return double.NaN;
        riskFreeRate/=100.00;
        double fractionOfYearHeld=1.00;
        if(yearEnd>DateTime.Now)
        {
          fractionOfYearHeld=GetFractionOfYearHeld(DateTime.Now,startOfYear);
          cumulativeYearInvestment=initialInvestment*Math.Pow(1.00+riskFreeRate,fractionOfYearHeld);
        }
        else cumulativeYearInvestment=initialInvestment*Math.Pow(1.00+riskFreeRate,fractionOfYearHeld);
      }
      foreach(PortfolioTrade portfolioTrade in tradesInYear)
      {
        riskFreeRate=YieldCurveDA.GetRiskFreeRate1Yr(portfolioTrade.TradeDate,10);    // suggestion: move this database call out of the inner loop
        if(double.NaN.Equals(riskFreeRate)) return double.NaN;
        riskFreeRate/=100.00;
        double fractionOfYearHeld=GetFractionOfYearHeld(portfolioTrade,yearEnd);
        double tradeInvestment=portfolioTrade.Exposure()*Math.Pow((1.00+riskFreeRate),fractionOfYearHeld);
        cumulativeYearInvestment+=tradeInvestment;
      }
      return cumulativeYearInvestment;
    }
    private static double GetFractionOfYearHeld(PortfolioTrade portfolioTrade,DateTime yearEnd)
    {
      DateGenerator dateGenerator=new DateGenerator();
      double fractionOfYearHeld=dateGenerator.DaysBetween(portfolioTrade.TradeDate,yearEnd)/365.00;
      return fractionOfYearHeld;
    }
    private static double GetFractionOfYearHeld(DateTime effectiveDate,DateTime yearEnd)
    {
      DateGenerator dateGenerator=new DateGenerator();
      double fractionOfYearHeld=dateGenerator.DaysBetween(effectiveDate,yearEnd)/365.00;
      return fractionOfYearHeld;
    }
  }
}
