using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using MarketData.MarketDataModel;
using MarketData.DataAccess;
using MarketData.Utils;
using MarketData.Generator.GainLoss;
using MarketData.MarketDataModel.GainLoss;
using MarketDataServer.Authorization;
using MarketData.Cache;
using MarketData.Generator;

namespace MarketDataServer.Controllers
{
  public class GainLossController : ApiController
  {
    private ActiveGainLossGenerator gainLossGenerator=new ActiveGainLossGenerator();
    public IEnumerable<GainLossSummaryItem> GetGainLoss(String token, DateTime selectedDate)
    {
      Console.WriteLine(String.Format("[{0:G}][GainLossController::GetGainLoss](String token, DateTime selectedDate)", DateTime.Now));
      if (!Authorizations.GetInstance().IsAuthorized(token)) return null;
      LocalPriceCache.GetInstance().Refresh();
      PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
      PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate);
      GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate);

// **** Add an aggregate entry
      GainLossSummaryItem gainLossSummaryTotals=new GainLossSummaryItem();
      gainLossSummaryTotals.Symbol="";
      gainLossSummaryTotals.CompanyName="Account Summary";
      if(null!=gainLossSummaryItems&&gainLossSummaryItems.Count>0)
      {
        gainLossSummaryTotals.Date=gainLossSummaryItems.Min(x => x.Date);
        gainLossSummaryTotals.Change=gainLossSummaryItems.Sum(x=>x.Change);
        gainLossSummaryTotals.CurrentGainLoss=gainLossSummaryItems.Sum(x => x.CurrentGainLoss);
        gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItems.Sum(x => x.PreviousGainLoss);
        gainLossSummaryTotals.ChangePercent=((gainLossSummaryTotals.CurrentGainLoss-gainLossSummaryTotals.PreviousGainLoss)/Math.Abs(gainLossSummaryTotals.PreviousGainLoss))*100.00;
      }
      else
      {
        gainLossSummaryTotals.Date = selectedDate;
        gainLossSummaryTotals.Change = 0.00;
        gainLossSummaryTotals.CurrentGainLoss = 0.00;
        gainLossSummaryTotals.PreviousGainLoss = 0.00;
        gainLossSummaryTotals.ChangePercent = 0.00;
      }
      gainLossSummaryItems.Insert(0,gainLossSummaryTotals);
// ****
      return gainLossSummaryItems;
    }
    public IEnumerable<GainLossSummaryItem> GetGainLoss(String token, DateTime selectedDate,String account)
    {
      Console.WriteLine(String.Format("[{0:G}][GainLossController::GetGainLoss](String token, DateTime selectedDate,String account)", DateTime.Now));
      LocalPriceCache.GetInstance().Refresh();
      if (!Authorizations.GetInstance().IsAuthorized(token)) return null;
      PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
      portfolioTrades=new PortfolioTrades(portfolioTrades.Where(x=>x.Account.Equals(account)).ToList());
      PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate);
      GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate);

      // **** Add an aggregate entry
      GainLossSummaryItem gainLossSummaryTotals = new GainLossSummaryItem();
      gainLossSummaryTotals.Symbol = "";
      gainLossSummaryTotals.CompanyName="Account Summary";
      if (null != gainLossSummaryItems && gainLossSummaryItems.Count > 0)
      {
        gainLossSummaryTotals.Date = gainLossSummaryItems.Min(x => x.Date);
        gainLossSummaryTotals.Change = gainLossSummaryItems.Sum(x => x.Change);
        gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItems.Sum(x => x.CurrentGainLoss);
        gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItems.Sum(x => x.PreviousGainLoss);
        gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00;
      }
      else
      {
        gainLossSummaryTotals.Date = selectedDate;
        gainLossSummaryTotals.Change = 0.00;
        gainLossSummaryTotals.CurrentGainLoss = 0.00;
        gainLossSummaryTotals.PreviousGainLoss = 0.00;
        gainLossSummaryTotals.ChangePercent = 0.00;
      }
      gainLossSummaryItems.Insert(0, gainLossSummaryTotals);
      // ****
      return gainLossSummaryItems;
    }
// *********
    public IEnumerable<GainLossSummaryItemDetail> GetGainLossWithDetail(String token, DateTime selectedDate)
    {
      Console.WriteLine(String.Format("[{0:G}][GainLossController::GetGainLossWithDetail](String token, DateTime selectedDate)", DateTime.Now));
      if (!Authorizations.GetInstance().IsAuthorized(token)) return null;
      LocalPriceCache.GetInstance().Refresh();
      PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
      PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate);
      GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate);

      List<GainLossSummaryItemDetail> gainLossSummaryItemDetailCollection = new List<GainLossSummaryItemDetail>();
      foreach (GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems)
      {
        GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem);
        portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol);
        double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield();
        DateTime currentDate = PricingDA.GetLatestDate(gainLossSummaryItem.Symbol);
        if (null == portfolioTrades || 0 == portfolioTrades.Count) continue;
        double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum();
        double exposure = portfolioTrades.Sum(x => x.Exposure());
        if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator();
        GainLossCollection gainLoss=gainLossGenerator.GenerateGainLoss(portfolioTrades);  // gainLoss contains the gain/loss from active positions.  Never includes dividends .. just positions
        GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault();
        gainLossSummaryItemDetail.Lots = portfolioTrades.Count;
        gainLossSummaryItemDetail.Shares = shares;
        gainLossSummaryItemDetail.Exposure = exposure;
        if (!double.IsNaN(weightAdjustedDividendYield))
        {
          gainLossSummaryItemDetail.DividendYield = weightAdjustedDividendYield;
          gainLossSummaryItemDetail.AnnualDividend = exposure * weightAdjustedDividendYield;
        }
        ParityElement parityElement = ParityGenerator.GenerateBreakEven(gainLossSummaryItem.Symbol);
        gainLossSummaryItemDetail.ParityElement = parityElement;
        if (null != parityElement)
        {
          gainLossSummaryItemDetail.AllTimeGainLossPercent = gainLossItem.GainLossPercent;
          gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent = parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100);
        }
        DateGenerator dateGenerator = new DateGenerator();
        DateTime priorDate = dateGenerator.FindPrevBusinessDay(currentDate);
        Price p1 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, currentDate);
        Price p2 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, priorDate);
        if (null == p2 && null != p1)
        {
          priorDate = dateGenerator.FindPrevBusinessDay(priorDate);
          p2 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, priorDate);
        }
        if (null != p1 && null != p2)
        {
          double change = (p1.Close - p2.Close) / p2.Close;
          gainLossSummaryItemDetail.LatestPrice = p1;
          gainLossSummaryItemDetail.PriceChange = change;
        }
        gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail);
      }

      // **** Add an aggregate entry
      GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail();
      gainLossSummaryTotals.Symbol = "";
      gainLossSummaryTotals.CompanyName = "Account Summary";
      if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0)
      {
        gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date);
        gainLossSummaryTotals.Exposure = gainLossSummaryItemDetailCollection.Sum(x => x.Exposure);
        gainLossSummaryTotals.Change = gainLossSummaryItemDetailCollection.Sum(x => x.Change);
        gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.CurrentGainLoss);
        gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.PreviousGainLoss);
        gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00;
        gainLossSummaryTotals.LatestPrice = new Price();
        gainLossSummaryTotals.PriceChange = 0;
      }
      else
      {
        gainLossSummaryTotals.Date = selectedDate;
        gainLossSummaryTotals.Change = 0.00;
        gainLossSummaryTotals.CurrentGainLoss = 0.00;
        gainLossSummaryTotals.PreviousGainLoss = 0.00;
        gainLossSummaryTotals.ChangePercent = 0.00;
        gainLossSummaryTotals.LatestPrice = new Price();
        gainLossSummaryTotals.PriceChange = 0;
      }
      gainLossSummaryItemDetailCollection.Insert(0, gainLossSummaryTotals);
      // ****
      return gainLossSummaryItemDetailCollection;
    }
    public IEnumerable<GainLossSummaryItemDetail> GetGainLossWithDetail(String token, DateTime selectedDate, String account)
    {
      Console.WriteLine(String.Format("[{0:G}][GainLossController::GetGainLossWithDetail](String token, DateTime selectedDate,String account)", DateTime.Now));
      if (!Authorizations.GetInstance().IsAuthorized(token)) return null;
      LocalPriceCache.GetInstance().Refresh();
      PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
      portfolioTrades = new PortfolioTrades(portfolioTrades.Where(x => x.Account.Equals(account)).ToList());
      PortfolioTrades tradesOnOrBefore = portfolioTrades.GetTradesOnOrBefore(selectedDate);
      GainLossSummaryItemCollection gainLossSummaryItems = new GainLossSummaryItemCollection(tradesOnOrBefore, selectedDate);

      List<GainLossSummaryItemDetail> gainLossSummaryItemDetailCollection=new List<GainLossSummaryItemDetail>();
      foreach(GainLossSummaryItem gainLossSummaryItem in gainLossSummaryItems)
      {
        GainLossSummaryItemDetail gainLossSummaryItemDetail = new GainLossSummaryItemDetail(gainLossSummaryItem);
        portfolioTrades = PortfolioDA.GetOpenTradesSymbol(gainLossSummaryItem.Symbol);
        double weightAdjustedDividendYield = portfolioTrades.GetWeightAdjustedDividendYield();
        DateTime currentDate = PricingDA.GetLatestDate(gainLossSummaryItem.Symbol);
        if(null==portfolioTrades||0==portfolioTrades.Count)continue;
        double shares = (from PortfolioTrade portfolioTrade in portfolioTrades select portfolioTrade.Shares).Sum();
        double exposure = portfolioTrades.Sum(x => x.Exposure());
        if(null==gainLossGenerator) gainLossGenerator=new ActiveGainLossGenerator();
        GainLossCollection gainLoss = gainLossGenerator.GenerateGainLoss(portfolioTrades);  // gainLoss contains the gain/loss from active positions.  Never includes dividends .. just positions
        GainLossItem gainLossItem = gainLoss.OrderByDescending(x => x.GainLossPercent).FirstOrDefault();
        gainLossSummaryItemDetail.Lots=portfolioTrades.Count;
        gainLossSummaryItemDetail.Shares=shares;
        gainLossSummaryItemDetail.Exposure=exposure;
        if (!double.IsNaN(weightAdjustedDividendYield))
        {
          gainLossSummaryItemDetail.DividendYield=weightAdjustedDividendYield;
          gainLossSummaryItemDetail.AnnualDividend=exposure * weightAdjustedDividendYield;
        }
        ParityElement parityElement = ParityGenerator.GenerateBreakEven(gainLossSummaryItem.Symbol);
        gainLossSummaryItemDetail.ParityElement=parityElement;
        if (null != parityElement)
        {
          gainLossSummaryItemDetail.AllTimeGainLossPercent=gainLossItem.GainLossPercent;
          gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent=parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100);
        }
        DateGenerator dateGenerator = new DateGenerator();
        DateTime priorDate = dateGenerator.FindPrevBusinessDay(currentDate);
        Price p1 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, currentDate);
        Price p2 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, priorDate);
        if (null == p2 && null != p1)
        {
          priorDate = dateGenerator.FindPrevBusinessDay(priorDate);
          p2 = PricingDA.GetPrice(gainLossSummaryItem.Symbol, priorDate);
        }
        if(null!=p1&&null!=p2)
        {
          double change = (p1.Close - p2.Close) / p2.Close;
          gainLossSummaryItemDetail.LatestPrice=p1;
          gainLossSummaryItemDetail.PriceChange=change;
        }
        gainLossSummaryItemDetailCollection.Add(gainLossSummaryItemDetail);
      }

      // **** Add an aggregate entry
      GainLossSummaryItemDetail gainLossSummaryTotals = new GainLossSummaryItemDetail();
      gainLossSummaryTotals.Symbol = "";
      gainLossSummaryTotals.CompanyName = "Account Summary";
      if (null != gainLossSummaryItemDetailCollection && gainLossSummaryItemDetailCollection.Count > 0)
      {
        gainLossSummaryTotals.Date = gainLossSummaryItemDetailCollection.Min(x => x.Date);
        gainLossSummaryTotals.Exposure=gainLossSummaryItemDetailCollection.Sum(x=>x.Exposure);
        gainLossSummaryTotals.Change = gainLossSummaryItemDetailCollection.Sum(x => x.Change);
        gainLossSummaryTotals.CurrentGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.CurrentGainLoss);
        gainLossSummaryTotals.PreviousGainLoss = gainLossSummaryItemDetailCollection.Sum(x => x.PreviousGainLoss);
        gainLossSummaryTotals.ChangePercent = ((gainLossSummaryTotals.CurrentGainLoss - gainLossSummaryTotals.PreviousGainLoss) / Math.Abs(gainLossSummaryTotals.PreviousGainLoss)) * 100.00;
        gainLossSummaryTotals.LatestPrice=new Price();
        gainLossSummaryTotals.PriceChange=0;
      }
      else
      {
        gainLossSummaryTotals.Date = selectedDate;
        gainLossSummaryTotals.Change = 0.00;
        gainLossSummaryTotals.CurrentGainLoss = 0.00;
        gainLossSummaryTotals.PreviousGainLoss = 0.00;
        gainLossSummaryTotals.ChangePercent = 0.00;
        gainLossSummaryTotals.LatestPrice = new Price();
        gainLossSummaryTotals.PriceChange = 0;
      }
      gainLossSummaryItemDetailCollection.Insert(0, gainLossSummaryTotals);
      // ****
      return gainLossSummaryItemDetailCollection;
    }

    public GainLossCompoundModelCollection GetCompoundGainLoss(String token, int selectedDays, bool includeDividends)
    {
      Console.WriteLine(String.Format("[{0:G}][GainLossController::GetCompundGainLoss](String token, int days)", DateTime.Now));
      if(!Authorizations.GetInstance().IsAuthorized(token)) return null;
      LocalPriceCache.GetInstance().Refresh();
      DividendPayments dividendPayments = null;
      PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
      GainLossGenerator gainLossGenerator=new GainLossGenerator();
//        GainLossGeneratorCum gainLossGeneratorCum=new GainLossGeneratorCum();
      if(includeDividends)dividendPayments=DividendPaymentDA.GetDividendPayments();
      ActiveGainLossGenerator activeGainLossGenerator=new ActiveGainLossGenerator();
      GainLossCollection gainLoss=activeGainLossGenerator.GenerateGainLoss(portfolioTrades);  // gainLoss contains the gain/loss from active positions.  Never includes dividends .. just positions
      TotalGainLossCollection totalGainLoss=null;
      if(null!=dividendPayments)totalGainLoss=gainLossGenerator.GenerateTotalGainLossWithDividends(portfolioTrades,dividendPayments);  
      else totalGainLoss=gainLossGenerator.GenerateTotalGainLoss(portfolioTrades);  
      GainLossCompoundModelCollection gainLossModelCollection=null;
      gainLossModelCollection=new GainLossCompoundModelCollection(gainLoss,totalGainLoss);
      if(-1==selectedDays)return gainLossModelCollection;
      int skip=gainLossModelCollection.Count-selectedDays;
      if(skip<0)return gainLossModelCollection;
      return new GainLossCompoundModelCollection(gainLossModelCollection.Skip(skip).ToList());
    }
  }
}
