require 'json'

class ResearchController < Users::BaseController
  # GET /research/
  def show
    accounts = current_user.accounts
    # @total_balance = accounts.map(&:equivalent_balance).sum
    @total_balance = 2000.00
    account = accounts.first
    balance_history = account.balance_history
    balance_data = balance_history.map do |timestamp, data|
      {
        timestamp: Time.at(timestamp).strftime('%F %H:%M'),
        balance: data[:balance]
      }
    end

    @chart_data = {
      data: [
        {
          timestamp: Time.now.strftime('%F %H:%M'),
          balance: @total_balance
        }
      ],
      xkey: 'timestamp',
      ykeys: ['balance'],
      labels: ['Balance']
    }.to_json

    @supported_asset_pairs = Account::SUPPORTED_ASSET_PAIRS
    @supported_exchanges = Account::SUPPORTED_EXCHANGES
    @portfolio_balance = account.portfolio_balance
  end

  # POST /run_backtest/
  def run_backtest
    accounts = current_user.accounts
    account = accounts.first
    # @start_balance = accounts.map(&:equivalent_balance).sum
    @start_balance = params['starting_amount'].to_i

    asset_pair = params['asset_pair']
    start_date = params['start_date']
    end_datetime = Time.strptime(params['end_date'], '%m/%d/%Y').end_of_day.to_i
    now = Time.now.end_of_hour.to_i
    start_time = Time.strptime(start_date, '%m/%d/%Y').to_i

    # fetch new data if its not in the table
    Ohlc.fetch_new_data(asset_pair)

    price_history = Ohlc.where(
      date_timestamp: start_time..end_datetime,
      asset_pair: asset_pair
    )

    # BEGIN ALGO
    cash = @start_balance
    asset = 0
    initial_asset = (cash / price_history[0][:weighted_average].to_f)
    purchase_price = 0
    purchase_fees = 0
    sell_price = 0
    interval = 14400 # 4hrs
    x = 20 # 12 * 4hrs => ~2 days
    last_x_end_time = price_history[0][:date_timestamp]
    last_x_start_time = last_x_end_time - interval * x
    last_x = Ohlc.where(
      date_timestamp: last_x_start_time..last_x_end_time,
      asset_pair: asset_pair
    ).to_a
    # intentionally getting rid of last and dummy filling
    # index 1
    last_x.prepend(last_x.pop)
    x_moving_average = average_price(last_x)
    last_x_size = last_x.size
    start_x_index = 0
    end_x_index = last_x_size - 1
    last_x += price_history

    balance_data = []

    price_history.each_with_index do |ph, index|
      bought = false
      sold = false
      ts = ph[:date_timestamp]
      asset_price = ph[:weighted_average].to_f
      maker_percentage = 0.0016
      taker_percentage = 0.0026

      # x_moving_average = average_price(last_x[start_x_index..end_x_index])
      old_price = last_x[start_x_index][:weighted_average].to_f
      new_price = last_x[end_x_index][:weighted_average].to_f
      x_moving_average = (x_moving_average * last_x_size - old_price + new_price) / last_x_size.to_f
      start_x_index += 1
      end_x_index += 1

      should_buy = (x_moving_average > 0) &&
                    (asset_price > x_moving_average) &&
                    cash > 0
      if should_buy
        # buy
        print "BOUGHT AT #{asset_price}\n"
        print "TIME #{Time.at(ts)}\n"
        print "\n"

        purchase_fees = (cash / asset_price) * taker_percentage
        asset = (cash / asset_price) - purchase_fees
        cash = 0
        purchase_price = asset_price
        bought = true
      end

      asset_value = asset * asset_price
      net_difference = (asset_price - purchase_price) * asset
      fees = asset_value * maker_percentage + purchase_fees

      # x = 60 # ~10 days
      # x_moving_average = average_price(ts - interval * x, ts, asset_pair)
      should_sell = (x_moving_average > 0) &&
                    (asset_price < x_moving_average) &&
                    asset > 0 &&
                    fees < net_difference

      if should_sell && !bought
        # sell
        print "SOLD AT #{asset_price}\n"
        print "TIME #{Time.at(ts)}\n"
        print "NET DIFFERENCE = #{net_difference}\n"
        print "FEES = #{fees}\n"

        print "NET PROFITS = #{net_difference - fees}\n"
        print "\n"

        cash = (asset * asset_price) * (1 - maker_percentage)
        asset = 0

        sell_price = asset_price
        sold = true
      end

      indicator_scaler = 1
      if bought
        indicator_scaler = 2
      elsif sold
        indicator_scaler = 0
      end

      balance_info = {
        timestamp: Time.at(ph[:date_timestamp]).strftime('%F %H:%M'),
        balance: (cash + (asset * asset_price)) * indicator_scaler,
        control: initial_asset * asset_price
      }
      balance_data.push(balance_info)
    end
    # END ALGO

    @chart_data = {
      data: balance_data,
      xkey: 'timestamp',
      ykeys: ['balance', 'control'],
      labels: ['Balance', 'Control']
    }.to_json

    respond_to do |format|
      @end_balance = balance_data.last[:balance]
      @net_return_dollars = @end_balance - @start_balance
      @net_return_percentage = (@end_balance - @start_balance) / (@start_balance) * 100
      format.js { render :run_backtest, status: :ok, locals: { chart_data: @chart_data } }
    end
  end

  def average_price(last_x)
      last_x.map { |data_point| data_point[:weighted_average].to_f }.reduce(:+) / last_x.size
  end

  def kraken_time_format(time)
    time.to_s.ljust(19, '0')[0..18].to_i
  end
end
