using eNavigator.Interfaces;
using MarketData.MarketDataModel;
using MarketData.MarketDataModel.GainLoss;
using MarketData.Service;
using MarketData.Utils;
using Newtonsoft.Json;
using System.Text;
using System.Web;

namespace eNavigator.Service
{
  public class MarketDataServiceClient : IMarketDataServiceClient
  {
    private readonly HttpClient httpClient;
    private readonly IStateContainer stateContainer;
    private List<Exception> exceptions = new List<Exception>();

    public MarketDataServiceClient(HttpClient httpClient,IStateContainer stateContainer)
    {
      this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
      this.stateContainer = stateContainer ?? throw new ArgumentNullException(nameof(stateContainer));
    }

    protected String AccessToken 
    { 
      get
      {
        return stateContainer.Coalesce<String>("Token", default(String));
      }
      set
      {
        stateContainer.InsertOrUpdate("Token", value);
      }
    }

    public async Task<bool> IsAuthorized()
    {
      if (default == AccessToken) return await Task.FromResult(false);
      return true;
    }    

    public async Task<bool> Ping()
    {
      bool result = false;
      try
      {
        Profiler profiler = new Profiler();
        Console.WriteLine($"Contacting host at {httpClient.BaseAddress}");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Ping/GetPing");
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        result = JsonConvert.DeserializeObject<bool>(json); 
        Console.WriteLine($"Ping took {profiler.End()}MS");
        return result;
      }
      catch(Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return result;
      }
    }

    public async Task<ServiceResult> GetSystemInfo()
    {
      try
      {
        StringBuilder sb = new StringBuilder();
        sb.Append("/api/Ping/GetSystemInfo");
        String json = await httpClient.GetStringAsync(sb.ToString());
        json=ToConformimgJson(json);
        String accessToken = JsonConvert.DeserializeObject<String>(json);
        return new ServiceResult(accessToken);
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> Login(String user, String password)
    {
      try
      {
        Profiler profiler = new Profiler();
        StringBuilder sb = new StringBuilder();
        user = Authorizations.Xor(user);
        password = Authorizations.Xor(password);
        sb.Append("api/Authorization/GetToken?").Append("user=").Append(user);
        sb.Append("&password=").Append(password);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        json=ToConformimgJson(json);
        AccessToken = JsonConvert.DeserializeObject<String>(json);
        if(default == AccessToken)return new ServiceResult(false);
        return new ServiceResult(AccessToken){ElapsedTimeMS=profiler.End()};      
      }
      catch(Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false);
      }
    }

    public async Task<ServiceResult> GetDistinctConsumerPriceIndices()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/PriceIndex/GetDistinctPriceIndices?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<String> priceIndices = JsonConvert.DeserializeObject<List<String>>(json);
        return new ServiceResult(priceIndices){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }   

    public async Task<ServiceResult> GetGetConsumerPriceIndex(String indexCode)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/PriceIndex/GetConsumerPriceIndex?").Append("token=").Append(AccessToken).Append("&").Append("indexCode=").Append(HttpUtility.UrlEncode(indexCode));
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<PriceIndex> priceIndices = JsonConvert.DeserializeObject<List<PriceIndex>>(json);
        return new ServiceResult(priceIndices){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }   

    public async Task<ServiceResult> GetLatestPremarketData(String market,DateTime marketDate)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/PreMarket/GetLatestPremarketData?").Append("token=").Append(AccessToken).Append("&").Append("market=").Append(HttpUtility.UrlEncode(market));
        sb.Append("&").Append("marketDate=").Append(marketDate);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<PremarketElement> premarketData = JsonConvert.DeserializeObject<List<PremarketElement>>(json);
        return new ServiceResult(premarketData){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }

    public async Task<ServiceResult> GetAvailableMarkets()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/PreMarket/GetAvailableMarkets?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<String> availableMarkets = JsonConvert.DeserializeObject<List<String>>(json);
        return new ServiceResult(availableMarkets){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }

    public async Task<ServiceResult> GetAvailableMarketDates(String market)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/PreMarket/GetAvailableMarketDates?").Append("token=").Append(AccessToken).Append("&").Append("market=").Append(HttpUtility.UrlEncode(market));
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<DateTime> availableMarketDates = JsonConvert.DeserializeObject<List<DateTime>>(json);
        return new ServiceResult(availableMarketDates){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }

    public async Task<ServiceResult> GetStopLimit(String symbol)
    {
        try
        {
          Profiler profiler = new Profiler();
          if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
          StringBuilder sb = new StringBuilder();
          sb.Append("api/Portfolio/GetStopLimit?").Append("token=").Append(AccessToken).Append("&").Append("symbol=").Append(symbol);
          Console.WriteLine(httpClient.BaseAddress+sb.ToString());
          String json = await httpClient.GetStringAsync(sb.ToString());
          if(null==json)return new ServiceResult(null);
          StopLimit stopLimit= JsonConvert.DeserializeObject<StopLimit>(json);
          return new ServiceResult(stopLimit){ElapsedTimeMS=profiler.End()};
        }
        catch (Exception exception)
        {                                                                                     
          exceptions.Add(exception);
          Console.WriteLine(exception.ToString());
          return new ServiceResult(false,exception.ToString());
        }
    }

    public async Task<ServiceResult> GetPortfolioTradesWithParityPrice(String symbol)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Portfolio/GetPortfolioTradesWithParityPrice?").Append("token=").Append(AccessToken).Append("&").Append("symbol=").Append(symbol);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        PortfolioTradesWithParityPrice portfolioTradesWithParityPrice= JsonConvert.DeserializeObject<PortfolioTradesWithParityPrice>(json);
        return new ServiceResult(portfolioTradesWithParityPrice){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetPositionsWithDescription()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false, "Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Portfolio/GetOpenPositionsWithDescription?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<PositionWithDescription> positionsWithDescription = JsonConvert.DeserializeObject<List<PositionWithDescription>>(json);
        return new ServiceResult(positionsWithDescription){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false, exception.ToString());
      }
    }

    public async Task<ServiceResult> GetWatchList(String watchList)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/WatchList/GetWatchList?").Append("token=").Append(AccessToken).Append("&").Append("watchList=").Append(watchList);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<String> symbols= JsonConvert.DeserializeObject<List<String>>(json);
        return new ServiceResult(symbols){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetHeadlines(DateTime headlineDate)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Headlines/GetHeadlines?").Append("token=").Append(AccessToken).Append("&").Append("headlineDate=").Append(headlineDate);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<Headline> headlines= JsonConvert.DeserializeObject<List<Headline>>(json);
        return new ServiceResult(headlines){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetHeadlineDates()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Headlines/GetHeadlineDates?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<String> headlineDates= JsonConvert.DeserializeObject<List<String>>(json);
        return new ServiceResult(headlineDates){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetLatestHeadlines()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized."){ElapsedTimeMS=profiler.Stop()};
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Headlines/GetLatestHeadlines?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<Headline> latestHeadlines= JsonConvert.DeserializeObject<List<Headline>>(json);
        return new ServiceResult(latestHeadlines){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetLatestPricingDate()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Price/GetLatestPricingDate?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        DateTime latestPricingDate= JsonConvert.DeserializeObject<DateTime>(json);
        return new ServiceResult(latestPricingDate){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetGainLossDetails(DateTime selectedDate,String account)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/GainLoss/GetGainLossWithDetail?").Append("token=").Append(AccessToken).Append("&selectedDate=").Append(selectedDate.ToShortDateString()).Append("&").Append("account=").Append(account);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<GainLossSummaryItemDetail> gainLossSummaryDetail = JsonConvert.DeserializeObject<List<GainLossSummaryItemDetail>>(json);
        return new ServiceResult(gainLossSummaryDetail){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {                                                                                     
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetGainLossDetails(DateTime selectedDate)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/GainLoss/GetGainLossWithDetail?").Append("token=").Append(AccessToken).Append("&selectedDate=").Append(selectedDate.ToShortDateString());
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<GainLossSummaryItemDetail> gainLossSummaryDetail = JsonConvert.DeserializeObject<List<GainLossSummaryItemDetail>>(json);
        return new ServiceResult(gainLossSummaryDetail){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetCompoundGainLoss(int selectedDays)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        bool includeDividends=false;
        sb.Append("api/GainLoss/GetCompoundGainLoss?").Append("token=").Append(AccessToken).Append("&selectedDays=").Append(selectedDays).Append("&").Append("includeDividends=").Append(includeDividends);;
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        GainLossCompoundModelCollection gainLossCompoundModelCollection = JsonConvert.DeserializeObject<GainLossCompoundModelCollection>(json);
        return new ServiceResult(gainLossCompoundModelCollection){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        exceptions.Add(exception);
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetAccountsWithOpenTrades()
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized()) return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Portfolio/GetAccountsWithOpenTrades?").Append("token=").Append(AccessToken);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<String> accounts = JsonConvert.DeserializeObject<List<String>>(json);
        return new ServiceResult(accounts){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return null;
      }
    }

    public async Task<ServiceResult> GetPrices(String symbol, int days)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized())return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Price/GetPrices?").Append("token=").Append(AccessToken).Append("&").Append("symbol=").Append(symbol).Append("&").Append("days=").Append(days);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<Price> prices = JsonConvert.DeserializeObject<List<Price>>(json);
        return new ServiceResult(prices){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetCompanyNameForSymbol(String symbol)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized())return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("api/Price/GetCompanyNameForSymbol?").Append("token=").Append(AccessToken).Append("&").Append("symbol=").Append(symbol);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        String companyName = JsonConvert.DeserializeObject<String>(json);
        return new ServiceResult(companyName){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }

    public async Task<ServiceResult> GetBollingerBands(String symbol,int dayCount)
    {
      try
      {
        Profiler profiler = new Profiler();
        if (!await IsAuthorized())return new ServiceResult(false,"Unauthorized.");
        StringBuilder sb = new StringBuilder();
        sb.Append("/api/Price/GetBollingerBands?").Append("token=").Append(AccessToken).Append("&").Append("symbol=").Append(symbol).Append("&").Append("dayCount=").Append(dayCount);
        Console.WriteLine(httpClient.BaseAddress+sb.ToString());
        String json = await httpClient.GetStringAsync(sb.ToString());
        List<BollingerBandElement> bollingerBands=JsonConvert.DeserializeObject<List<BollingerBandElement>>(json);
        return new ServiceResult(bollingerBands){ElapsedTimeMS=profiler.End()};
      }
      catch (Exception exception)
      {
        Console.WriteLine(exception.ToString());
        return new ServiceResult(false,exception.ToString());
      }
    }
    private static String ToConformimgJson(String json)
    {
      if(String.IsNullOrEmpty(json))return json;
      if(json.StartsWith("\"") && json.EndsWith("\""))return json;
      if(json.StartsWith("[") && json.EndsWith("]"))return json;
      if(json.StartsWith("{") && json.EndsWith("}"))return json;
      return Utility.AddQuotes(json);
    }
  }
}
