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;
using MarketData;
using LogLevel = MarketData.LogLevel;
using Microsoft.AspNetCore.Mvc;

namespace MarketDataServer.Controllers
{
  /// <summary>
  /// GainLossController :
  /// GetGainLossByDate(String token,DateTime selectedDate)
  /// GetGainLossByDateAndAccount(String token,DateTime selectedDate,String account)
  /// GetGainLossWithDetailByDate(String token,DateTime selectedDate)
  /// GetGainLossWithDetailByDateAndAccount(String token, DateTime selectedDate, String account)
  /// GetCompoundGainLoss(String token, int selectedDays, bool includeDividends)
  /// </summary>

  [ApiController]
  [Route("api/[controller]/[action]")]
  public class GainLossController : ControllerBase
  {
    private ActiveGainLossGenerator gainLossGenerator=new ActiveGainLossGenerator();

    [HttpGet]
    public IEnumerable<GainLossSummaryItem> GetGainLossByDate(String token,DateTime selectedDate)
    {
      Profiler profiler = new Profiler();
      try
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Start");
        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;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}");
        return null;
      }
      finally
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
      }
    }

    [HttpGet]
    public IEnumerable<GainLossSummaryItem> GetGainLossByDateAndAccount(String token,DateTime selectedDate,String account)
    {
      Profiler profiler = new Profiler();
      try
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Start");
        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;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}");
        return null;
      }
      finally
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
      }
    }

    /// <summary>
    /// GetGainLossWithDetailByDate - Optimizing calls to DA methods
    /// </summary>
    /// <param name="token"></param>
    /// <param name="selectedDate"></param>
    /// <returns></returns>
    [HttpGet]
    public IEnumerable<GainLossSummaryItemDetail> GetGainLossWithDetailByDate(String token,DateTime selectedDate)
    {
      Profiler profiler = new Profiler();
      try
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Start");
        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<String> symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList();
        Dictionary<String,DateTime> latestDates = PricingDA.GetLatestDates(symbols);

        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 = latestDates[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;
          }
          Prices prices = LocalPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection
          Price p1 = prices.Count>0?prices[0]:default;
          Price p2 = prices.Count>1?prices[1]:default;
          PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList());
          ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1);
          gainLossSummaryItemDetail.ParityElement = parityElement;
          if (null != parityElement)
          {
            gainLossSummaryItemDetail.AllTimeGainLossPercent = gainLossItem.GainLossPercent;
            gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent = parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100);
          }
          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;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}");
        return null;
      }
      finally
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
      }
    }

    [HttpGet]
    public IEnumerable<GainLossSummaryItemDetail> GetGainLossWithDetailByDateAndAccount(String token, DateTime selectedDate, String account)
    {
      Profiler profiler = new Profiler();
      try
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Start");
        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<String> symbols = gainLossSummaryItems.Select(x => x.Symbol).ToList();
        Dictionary<String,DateTime> latestDates = PricingDA.GetLatestDates(symbols);        

        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 = latestDates[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;
          }
          Prices prices = LocalPriceCache.GetInstance().GetPrices(gainLossSummaryItem.Symbol, currentDate, 3); // cache should be refreshed after GainLossSummaryItemCollection
          Price p1 = prices.Count>0?prices[0]:default;
          Price p2 = prices.Count>1?prices[1]:default;
          PortfolioTrades symbolTrades = new PortfolioTrades(portfolioTrades.Where(x=>x.Symbol.Equals(gainLossSummaryItem.Symbol)).ToList());
          ParityElement parityElement = ParityGenerator.GenerateBreakEven(symbolTrades, p1);
          gainLossSummaryItemDetail.ParityElement=parityElement;
          if (null != parityElement)
          {
            gainLossSummaryItemDetail.AllTimeGainLossPercent=gainLossItem.GainLossPercent;
            gainLossSummaryItemDetail.PercentDistanceFromAllTimeGainLossPercent=parityElement.ParityOffsetPercent - (gainLossItem.GainLossPercent / 100);
          }
          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;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}");
        return null;
      }
      finally
      {
          MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
      }
    }

    [HttpGet(Name = "GetCompoundGainLoss")]
    public GainLossCompoundModelCollection GetCompoundGainLoss(String token, int selectedDays, bool includeDividends)
    {
      Profiler profiler = new Profiler();
      try
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Start");
        if(!Authorizations.GetInstance().IsAuthorized(token)) return null;
        LocalPriceCache.GetInstance().Refresh();
        DividendPayments dividendPayments = null;
        PortfolioTrades portfolioTrades = PortfolioDA.GetTrades();
        GainLossGenerator gainLossGenerator=new GainLossGenerator();
        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());
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Exception:{exception.ToString()}");
        return null;
      }
      finally
      {
        MDTrace.WriteLine(LogLevel.DEBUG,$"Done, total took {profiler.End()} (ms)");
      }
    }
  }
}
