using Axiom.Interpreter;
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.CMTrend
{
  public class CMTTrendGenerator
  {
    private CMTTrendGenerator()
    {
    }
    public static CMTGeneratorResult GenerateCMTCandidates(DateTime tradeDate,CMTParams cmtParams,List<String> symbolsHeld)
    {
      CMTGeneratorResult cmtGeneratorResult=new CMTGeneratorResult();
      try
      {
        List<String> symbols=PricingDA.GetSymbols();

// Filter out symbols where we do not have a price on trade date
        Profiler profiler = new Profiler();
        Dictionary<String,DateTime> latestDates = PricingDA.GetLatestDates(symbols);
        symbols=symbols.Where(x => latestDates.ContainsKey(x) && latestDates[x].Date>=tradeDate.Date).ToList();
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded pricing dates in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

// Prefetch a subset of fundamentals where each fundamental.asof is no greater than tradeDate
        profiler.Reset();
        FundamentalsV2 fundamentals = FundamentalDA.GetFundamentalsMaxDateV2(tradeDate);
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded fundamentals in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

// Prefetch the Company Profiles
        profiler.Reset();
        Dictionary<String,CompanyProfile> companyProfiles = CompanyProfileDA.GetCompanyProfiles(symbols);
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded company profiles in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

// Prefetch the pricing dates required for 200 day moving average.
        profiler.Reset();
        DateGenerator dateGenerator = new DateGenerator();
        DateTime historicalDate=dateGenerator.GenerateHistoricalDate(tradeDate,cmtParams.DMA200Horizon+10);
        List<DateTime> historicalDates=PricingDA.GetPricingDatesBetween(historicalDate,tradeDate);
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded moving average dates in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

// Prefetch the EPS time series
        profiler.Reset();
        Dictionary<String,TimeSeriesCollection> epsTimeSeriesCollectionDictionary = FundamentalDA.GetEPS(symbols,tradeDate,3);
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded EPS Time Series in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

// Prefetch the profit margin time series
        profiler.Reset();
        Dictionary<String,TimeSeriesCollection> profitMarginTimeSeriesCollectionDictionary = IncomeStatementDA.GetProfitMarginMaxAsOf(symbols,tradeDate,3,IncomeStatement.PeriodType.Quarterly);
        MDTrace.WriteLine(LogLevel.DEBUG,$"Loaded Profit Margin Time Series in {Utility.FormatNumber(profiler.End(),0,true)} (ms)");

        for(int index=0;index<symbols.Count;index++)
        {
          String symbol=symbols[index];
          if(0==(index%1000)) Console.WriteLine("Processing item {0} of {1}",index+1,symbols.Count);

          FundamentalV2 fundamental = default;
          if(fundamentals.ContainsKey(symbol))fundamental=fundamentals[symbol];

          CompanyProfile companyProfile = default;
          if(companyProfiles.ContainsKey(symbol))companyProfile = companyProfiles[symbol];

          TimeSeriesCollection epsTimeSeriesCollection = default;
          if(epsTimeSeriesCollectionDictionary.ContainsKey(symbol))epsTimeSeriesCollection=epsTimeSeriesCollectionDictionary[symbol];

          TimeSeriesCollection profitMarginTimeSeriesCollection = default;
          if(profitMarginTimeSeriesCollectionDictionary.ContainsKey(symbol))profitMarginTimeSeriesCollection=profitMarginTimeSeriesCollectionDictionary[symbol];

          CMTCandidate cmtCandidate=CMTCandidateGenerator.GenerateCandidate(symbol,tradeDate,cmtParams,fundamental,companyProfile,historicalDates,epsTimeSeriesCollection,profitMarginTimeSeriesCollection,symbolsHeld);
          if(null==cmtCandidate) continue;
          if(cmtCandidate.Violation) cmtGeneratorResult.CMTCandidatesWithViolation.Add(cmtCandidate);
          else cmtGeneratorResult.CMTCandidates.Add(cmtCandidate);
        }
        if(null==cmtGeneratorResult.CMTCandidates||0==cmtGeneratorResult.CMTCandidates.Count)
        {
          MDTrace.WriteLine(LogLevel.DEBUG,String.Format("GenerateCMTCandidates.  No candidates generated for trade date: {0}",tradeDate));
          cmtGeneratorResult.Success=true;
          return cmtGeneratorResult;
        }
        MDTrace.WriteLine(LogLevel.DEBUG,String.Format("Generated {0} candidates.",cmtGeneratorResult.CMTCandidates.Count));
        cmtGeneratorResult.CMTCandidates.Sort();
        MDTrace.WriteLine(LogLevel.DEBUG,CMTCandidate.Header());
        foreach(CMTCandidate candidate in cmtGeneratorResult.CMTCandidates) MDTrace.WriteLine(LogLevel.DEBUG,candidate.ToString());
        cmtGeneratorResult.Success=true;
        return cmtGeneratorResult;
      }
      catch(Exception exception)
      {
        MDTrace.WriteLine(LogLevel.DEBUG,exception.ToString());
        cmtGeneratorResult.Success=false;
        cmtGeneratorResult.Messages.Add(exception.ToString());
        return cmtGeneratorResult;
      }
    }
  }
}

