# frozen_string_literal: true

require 'logger'

module Cryptum
  # This plugin is used to Refresh the Cryptum console UI
  module UI
    module OrderExecution
      # Supported Method Parameters::
      # Cryptum::UI::Candle.refresh(
      #   order_book: 'required - Order Book Data Structure',
      #   event: 'required - Event from Coinbase Web Socket'
      # )

      public_class_method def self.refresh(opts = {})
        option_choice = opts[:option_choice]
        order_execute_win = opts[:order_execute_win]
        env = opts[:env]
        event_history = opts[:event_history]
        key_press_event = opts[:key_press_event]
        indicator_status = opts[:indicator_status]
        bot_conf = opts[:bot_conf]
        fiat_portfolio_file = opts[:fiat_portfolio_file]

        event_type = event_history.event_type if option_choice.autotrade
        event_side = event_history.event[:side].to_s.to_sym if option_choice.autotrade
        event_reason = event_history.event[:reason].to_s.to_sym if option_choice.autotrade

        ticker_price = event_history.order_book[:ticker_price].to_f
        open_24h = event_history.order_book[:open_24h].to_f
        this_product = event_history.order_book[:this_product]
        # base_min_size = this_product[:base_min_size]
        min_market_funds = this_product[:min_market_funds]
        base_increment = this_product[:base_increment]
        quote_increment = this_product[:quote_increment]
        # crypto_smallest_size_to_buy = base_min_size.to_s.split('.')[-1].length
        crypto_smallest_decimal = base_increment.to_s.split('.')[-1].length
        fiat_smallest_decimal = quote_increment.to_s.split('.')[-1].length
	# crypto_smallest_size_to_buy = format(
        #   "%0.#{crypto_smallest_decimal}f",
        #   min_market_funds.to_f / ticker_price
        # )
        # crypto_smallest_size_to_buy = crypto_smallest_size_to_buy.to_s.split('.')[-1].length

        last_three_prices_arr = []
        last_ticker_price = event_history.order_book[:ticker_price].to_f
        second_to_last_ticker_price = event_history.order_book[:ticker_price_second_to_last].to_f
        third_to_last_ticker_price = event_history.order_book[:ticker_price_third_to_last].to_f
        last_three_prices_arr.push(last_ticker_price)
        last_three_prices_arr.push(second_to_last_ticker_price)
        last_three_prices_arr.push(third_to_last_ticker_price)
        limit_price = last_three_prices_arr.sort[1]
        return event_history unless limit_price.positive?

        tpm = bot_conf[:target_profit_margin_percent].to_f
        tpm_cast_as_decimal = tpm / 100

        order_history_meta = event_history.order_book[:order_history_meta]
        order_history = event_history.order_book[:order_history] if option_choice.autotrade

        if event_history.order_book[:order_plan].length.positive?
          if event_history.order_ready
            event_history.order_book[:last_order_exec] = Time.now.strftime(
              '%Y-%m-%d %H:%M:%S.%N%z'
            )
          end

          # BUY
          margin_percent_open_24h = (1 - (open_24h / ticker_price)) * 100
          cast_margin_to_sec = margin_percent_open_24h * 0.1

          # Reset times to max or default depending on
          # BULL or BEAR market trend
          if cast_margin_to_sec.positive?
            event_history.bullish_trend = true
            event_history.time_between_orders = event_history.time_between_orders_max
          end

          if cast_margin_to_sec.negative? &&
             event_history.bullish_trend

            event_history.bullish_trend = false
            event_history.time_between_orders = event_history.time_between_orders_reset
          end

          if event_history.order_ready &&
             indicator_status.action_signal == :buy &&
             !event_history.red_pill

            if option_choice.autotrade
              this_order = event_history.order_book[:order_plan].first

              price = format(
                "%0.#{fiat_smallest_decimal}f",
                limit_price
              )

              size = this_order[:invest].to_f / limit_price

              loop do
                size = size.to_f + base_increment.to_f
                break if (size.to_f * price.to_f) > min_market_funds.to_f
              end

              size = format(
                "%0.#{crypto_smallest_decimal}f",
                size
              )

              # size = size.to_i.floor if base_increment.to_i >= 1

              # size = base_min_size if size.to_f < base_min_size.to_f
              # size = min_market_funds if size.to_f < min_market_funds.to_f

              fiat_invested_this_order = size.to_f * price.to_f

              fiat_portfolio = event_history.order_book[:fiat_portfolio]
              fiat_balance = format('%0.2f', fiat_portfolio.first[:balance])
              fiat_avail_for_trade = format('%0.2f', fiat_portfolio.first[:available])

              event_history.red_pill = true if fiat_invested_this_order > fiat_avail_for_trade.to_f
                                               

              unless event_history.red_pill
                event_history = Cryptum::API.submit_limit_order(
                  option_choice: option_choice,
                  env: env,
                  price: price,
                  size: size,
                  buy_or_sell: :buy,
                  event_history: event_history,
                  bot_conf: bot_conf
                )
              end
            else
              this_order = event_history.order_book[:order_plan].shift
              # Mock Order ID
              this_order[:buy_order_id] = format(
                '%0.6i',
                Random.rand(0..999999)
              )

              this_order[:price] = limit_price.to_s
              this_order[:size] = format(
              "%0.#{crypto_smallest_decimal}f",
                this_order[:invest].to_f / limit_price
              )

              targ_price = limit_price + (limit_price * tpm_cast_as_decimal)
              this_order[:tpm] = format(
                '%0.2f',
                tpm
              )
              this_order[:target_price] = format(
                "%0.#{fiat_smallest_decimal}f",
                targ_price
              )
              this_order[:color] = :cyan
              order_history_meta.push(this_order)
            end
            # Increment n Seconds between buys to
            # account for bearish and bullish trends
            dynamic_time_increment = cast_margin_to_sec * -1

            # Lets also take balance into play
            balance_as_arbitrary_float = fiat_avail_for_trade.to_f / 1_000_000
            tbo = dynamic_time_increment - balance_as_arbitrary_float

            event_history.time_between_orders += tbo

            # Time between orders should never
            # be less than event_history.time_between_orders_min
            event_history.time_between_orders = event_history.time_between_orders_min if event_history.time_between_orders < event_history.time_between_orders_min
            # Time between orders should never
            # be more than event_history.time_between_orders_max
            event_history.time_between_orders = event_history.time_between_orders_max if event_history.time_between_orders > event_history.time_between_orders_max
          end

          # SELL
          # Once buy arders are fulfilled submit a
          # limit sell order for fulfillment
          unless option_choice.autotrade
            # Testing logic via Mock
            event_type_arr = [:received, :open, :done]
            last_et_index = event_type_arr.length - 1
            rand_et_index = Random.rand(0..last_et_index)
            event_type = event_type_arr[rand_et_index]

            event_side_arr = [:buy, :sell]
            last_es_index = event_side_arr.length - 1
            rand_es_index = Random.rand(0..last_es_index)
            event_side = event_type_arr[rand_es_index].to_s.to_sym
            event_reason = 'mock'
          end
        end

        # Update Completed Sell Orders w/ Green
        if event_type == :open &&
           event_side == :buy

          buy_order_id = event_history.event[:order_id]
          order_history_meta.each do |meta|
            meta[:color] = :red if meta[:buy_order_id] == buy_order_id
          end
        end
          
        if event_type == :done &&
           event_side == :buy &&
           event_reason == :canceled

          buy_order_id = event_history.event[:order_id]
          order_history_meta.each do |meta|
            next unless meta[:buy_order_id] == buy_order_id
            buy_done_at_hash_arr = order_history.select do |oh|
              oh[:id] == meta[:buy_order_id]
            end
            meta[:done_at] = Time.now.strftime('%Y-%m-%d %H:%M:%S.%N%z')
            meta[:color] = :black
          end
        end

        if event_type == :done &&
           event_side == :buy &&
           event_reason != :canceled

          if option_choice.autotrade
            order_ready_to_sell_arr = order_history_meta.select do |meta|
              meta[:buy_order_id] == event_history.event[:order_id]
            end
          else
            last_index = order_history_meta.length - 1
            rand_index = Random.rand(0..last_index)
            order_ready_to_sell_arr = [
              order_history_meta[rand_index]
            ]
          end

          if order_ready_to_sell_arr.length.positive?
            order_ready_to_sell = order_ready_to_sell_arr.first
            buy_order_id = order_ready_to_sell[:buy_order_id]

            if option_choice.autotrade
              price = format(
                "%0.#{fiat_smallest_decimal}f",
                order_ready_to_sell[:target_price]
              )

              size = order_ready_to_sell[:size]

              Cryptum::API.submit_limit_order(
                option_choice: option_choice,
                env: env,
                price: price,
                size: size,
                buy_or_sell: :sell,
                event_history: event_history,
                bot_conf: bot_conf,
                buy_order_id: buy_order_id
              )
            else
              sell_order_id = format(
                '%0.2i',
                Random.rand(0..999999)
              )

              event_history.order_book[:order_history_meta].each do |meta|
                if meta[:buy_order_id] == buy_order_id
                  meta[:sell_order_id] = sell_order_id
                  meta[:color] = :yellow
                end
              end
            end
          end
        end
        
        # Update Canceled Sell Orders w/ Black &&
        # Include done_at Timestamp for 24h gain calc
        if event_type == :done &&
           event_side == :sell &&
           event_reason == :canceled

          sell_order_id = event_history.event[:order_id]
          order_history_meta.each do |meta|
            next unless meta[:sell_order_id] == sell_order_id
            sell_done_at_hash_arr = order_history.select do |oh|
              oh[:id] == meta[:sell_order_id]
            end
            meta[:done_at] = Time.now.strftime('%Y-%m-%d %H:%M:%S.%N%z')

            # TODO: Retry sell order if the original sell order expires.

            # Reinitiate GTFO if the previous GTFO Order Expires.
            terminal_win.key_press_event.key_g = true if meta[:color] == :magenta
            meta[:color] = :black
          end
        end

        # Update Completed Sell Orders w/ Green &&
        # Include done_at Timestamp for 24h gain calc
        if event_type == :done &&
           event_side == :sell &&
           event_reason != :canceled

          sell_order_id = event_history.event[:order_id]
          order_history_meta.each do |meta|
            next unless meta[:sell_order_id] == sell_order_id
            sell_done_at_hash_arr = order_history.select do |oh|
              oh[:id] == meta[:sell_order_id]
            end
            meta[:done_at] = Time.now.strftime('%Y-%m-%d %H:%M:%S.%N%z')
            # meta[:done_at] = sell_done_at_hash_arr.first[:done_at] unless sell_done_at_hash_arr.empty?
            meta[:color] = :green
          end
        end
          
        # OK, now let's tally up everything...
        twenty_four_hrs_ago = Time.now - 86400

        # Snag all sold orders
        oh_meta_sold_arr = order_history_meta.select do |ohm|
          ohm[:color].to_sym == :green && ohm.key?(:done_at)
        end

        # Snag all sold orders within past 24 hrs
        ohm_sold_twenty_four_arr = []
        unless oh_meta_sold_arr.empty?
          ohm_sold_twenty_four_arr = oh_meta_sold_arr.select do |o|
            Time.parse(o[:done_at]) >= twenty_four_hrs_ago
          end
        end
        order_hist_meta_sold = ohm_sold_twenty_four_arr.length

        # Snag all expired orders
        oh_meta_expired_arr = order_history_meta.select do |ohm|
          ohm[:color].to_sym == :black && ohm.key?(:done_at)
        end

        # Snag all expired orders within past 24 hrs
        ohm_expire_twenty_four_arr = []
        unless oh_meta_expired_arr.empty?
          ohm_expire_twenty_four_arr = oh_meta_expired_arr.select do |o|
            Time.parse(o[:done_at]) >= twenty_four_hrs_ago
          end
        end
        order_hist_meta_expired = ohm_expire_twenty_four_arr.length

        # Calculate gains within past 24 hrs
        gains_24h_sum = ohm_sold_twenty_four_arr.map do |ohms|
          ohms[:profit].to_f
        end.sum

        gains_24h_out = Cryptum.beautify_large_number(
          value: format(
            '%0.2f',
            gains_24h_sum
          )
        )

        total_to_sell = order_history.select do |oh|
          oh[:status].to_sym == :open &&
          oh[:side].to_sym == :sell
        end.length
        total_to_sell = "#{total_to_sell}+" if total_to_sell == 1000

        # TODO: Everything Above this Line Needs to be Indicators ^

        # UI
        col_just1 = (Curses.cols - Cryptum::UI.col_first) - 1
        col_just3 = (Curses.cols - Cryptum::UI.col_third) - 1

        Cryptum::UI.detect_key_press_in_ui(
          key_press_event: key_press_event,
          ui_win: order_execute_win
        )

        # ROW 1
        out_line_no = 0
        line_color = :white
        style = :bold
        line_color = :red if event_history.order_execute_win_highlighted

        Cryptum::UI.line(
          ui_win: order_execute_win,
          out_line_no: out_line_no,
          color: line_color
        )

        # ROW 2
        out_line_no += 1
        order_execute_win.setpos(out_line_no, Cryptum::UI.col_first)
        order_execute_win.clrtoeol
        Cryptum::UI.colorize(
          ui_win: order_execute_win,
          color: :white,
          style: style,
          string: 'Open Sell Orders | 24 Hr Stats:'
        )

        order_execute_win.setpos(out_line_no, Cryptum::UI.col_third)
        Cryptum::UI.colorize(
          ui_win: order_execute_win,
          color: :white,
          style: style,
          string: "#{total_to_sell} | #{order_hist_meta_expired} Expired | #{order_hist_meta_sold} Sold | $#{gains_24h_out} Gained".rjust(
            col_just3,
            '.'
          )
        )

        # ROW 3
        out_line_no += 1
        Cryptum::UI.line(
          ui_win: order_execute_win,
          out_line_no: out_line_no,
          color: line_color
        )


        # ROWS 4-10
        first_row = event_history.order_execute_line
        last_row = first_row + 6
        if last_row >= order_history_meta.length
          last_row = order_history_meta.length - 1
          first_row = last_row - 6
          event_history.order_execute_line = first_row
        end

        order_history_meta.reverse[first_row..last_row].each do |meta|
          out_line_no += 1
          style = :normal
          style = :highlight if event_history.order_execute_win_highlighted
          risk_alloc_out = Cryptum.beautify_large_number(
            value: meta[:risk_alloc]
          )
          invest_out = Cryptum.beautify_large_number(
            value: meta[:invest]
          )
          price_out = Cryptum.beautify_large_number(
            value: meta[:price]
          )
          size_out = Cryptum.beautify_large_number(
            value: meta[:size]
          )
          target_price_out = Cryptum.beautify_large_number(
            value: meta[:target_price]
          )
          profit_out = Cryptum.beautify_large_number(
            value: meta[:profit]
          )
          plan_no = "#{meta[:plan_no]}|"

          buy_created_at_hash_arr = order_history.select do |oh|
            oh[:id] == meta[:buy_order_id]
          end

          buy_created_at = '__-__ __:__:__'
          unless buy_created_at_hash_arr.empty?
            buy_created_at = Time.parse(
              buy_created_at_hash_arr.first[:created_at]
            ).strftime('%m-%d %H:%M:%S')
          end

          invest = "$#{invest_out} @ "
          tick = "$#{price_out} = "
          size = "*#{size_out} + "
          tpm_out = "#{meta[:tpm]}% = "
          targ_tick = "$#{target_price_out}"
          profit = "|Profit: $#{meta[:profit]}"

          order_exec_ln = "#{plan_no}#{buy_created_at}|#{invest}#{tick}#{size}#{tpm_out}#{targ_tick}#{profit}"

          order_execute_win.setpos(out_line_no, Cryptum::UI.col_first)
          order_execute_win.clrtoeol
          Cryptum::UI.colorize(
            ui_win: order_execute_win,
            color: meta[:color],
            style: style,
            string: order_exec_ln.ljust(col_just1, '.')
          )
        end

        Cryptum::UI.line(
          ui_win: order_execute_win,
          out_line_no: out_line_no,
          color: line_color
        )

        order_execute_win.refresh

        # Reset Order Ready Boolean
        event_history.order_ready = false

        event_history
      rescue Interrupt
        # Exit Gracefully if CTRL+C is Pressed During Session
        Cryptum.exit_gracefully(which_self: self)
      rescue StandardError => e
        raise e
      end

      # Display Usage for this Module

      public_class_method def self.help
        puts "USAGE:
         #{self}.refresh(
           order_book: 'required - Order Book Data Structure',
           event: 'required - Event from Coinbase Web Socket'
         )
        "
      end
    end
  end
end