# frozen_string_literal: true

require 'logger'

module Cryptum
  # This plugin is used to Refresh the Cryptum console UI
  module UI
    module OrderPlan
      # 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_plan_win = opts[:order_plan_win]
        event_history = opts[:event_history]
        indicator_status = opts[:indicator_status]
        bot_conf = opts[:bot_conf]

        ticker_price = event_history.order_book[:ticker_price].to_f
        return event_history unless ticker_price.positive?

        market_trend_color = plan_color = :white

        this_product = event_history.order_book[:this_product]
        base_increment = this_product[:base_increment]
        min_market_funds = this_product[:min_market_funds]

        crypto_smallest_decimal = base_increment.to_s.split('.')[-1].length

        autotrade_percent = bot_conf[:autotrade_portfolio_percent].to_f
        autotrade_cast_as_decimal = autotrade_percent / 100

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

        crypto_currency = option_choice.symbol.to_s.upcase.split('_').first.to_sym
        portfolio = event_history.order_book[:portfolio]
        this_account = portfolio.select do |account|
          account[:currency] == crypto_currency.to_s
        end
        raise "ID for Crypto Currency, #{crypto_currency} Not Found" if this_account.empty?

        balance = format("%0.#{crypto_smallest_decimal}f", this_account.first[:balance])
        fiat_portfolio = event_history.order_book[:fiat_portfolio]
        total_holdings = format('%0.2f', fiat_portfolio.first[:total_holdings])
        fiat_avail_for_trade = format('%0.2f', fiat_portfolio.first[:available])

        fiat_budget = fiat_avail_for_trade.to_f

        order_plan = event_history.order_book[:order_plan]

        current_crypto_fiat_value = format(
          '%0.2f',
          balance.to_f * ticker_price
        )

        crypto_invested_percent = format(
          '%0.2f',
          current_crypto_fiat_value.to_f.fdiv(total_holdings.to_f) * 100
        )

        event_history.red_pill = true if crypto_invested_percent.to_f > autotrade_percent

        # SAUCE 2
        # Generating and/or recalculating order plan
        if event_history.recalculate_order_plan || (
             crypto_invested_percent.to_f <= autotrade_percent &&
             autotrade_percent.positive? && (
               order_plan.empty? || (
                 indicator_status.action_signal == :buy &&
                 indicator_status.last_action_signal == :sell
               )
             )
           )

          event_history.red_pill = false
          if indicator_status.action_signal == :buy &&
             indicator_status.last_action_signal == :sell

            order_plan = []
            # Reset time between orders
            event_history.time_between_orders = event_history.time_between_orders_reset
            event_history.plan_no += 1
          end

          plan_no_slice = 1
          plan_no = event_history.plan_no
          # Sum up currently invested amount so we don't
          # exceed autotrade % for next order plan generation
          allocation_decimal = 0.0
          risk_target = fiat_budget * autotrade_cast_as_decimal
          risk_alloc = risk_target

          # TODO: Order Plan <= event_history.max_open_sell_orders
          previous_slice_fiat_investing = 0.00
          last_slice_detected = false
          loop do
            # Calculate min order size
            allocation_decimal += 0.0001
            fiat_investing = risk_alloc * allocation_decimal

            next unless fiat_investing > min_market_funds.to_f

            # Increase amount invested in each slice as we increment
            # plan numbers (i.e. slices) in the order plan cycle
            slice_alloc = fiat_budget * allocation_decimal
            fiat_investing = slice_alloc + previous_slice_fiat_investing

            risk_alloc = fiat_budget * autotrade_cast_as_decimal
            allocation_percent = allocation_decimal * 100

            break if order_plan.map { |op| op[:invest].to_f }.sum > risk_target ||
                     allocation_percent > 100 ||
                     last_slice_detected

            next unless fiat_budget >= min_market_funds.to_f

            if fiat_investing > fiat_budget
              last_slice_detected = true
              fiat_investing = fiat_budget
            end

            fiat_returning = fiat_investing + (
              fiat_investing * tpm_cast_as_decimal
            )
            profit = fiat_returning - fiat_investing

            this_plan_no = "#{plan_no}.#{plan_no_slice}"
            if event_history.recalculate_order_plan
              order_slice = order_plan.find do |order|
                order[:plan_no] == this_plan_no
              end
            end
            order_slice ||= {}
            order_slice[:plan_no] = this_plan_no
            order_slice[:fiat_available] = format('%0.2f', fiat_budget)
            order_slice[:risk_alloc] = format('%0.2f', risk_alloc)
            order_slice[:allocation_decimal] = format(
              '%0.8f',
              allocation_decimal
            )
            order_slice[:allocation_percent] = format(
              '%0.2f',
              allocation_percent
            )
            order_slice[:slice_alloc] = format('%0.2f', slice_alloc)
            order_slice[:risk_target] = format('%0.2f', risk_target)
            order_slice[:previous_slice_invest] = format(
              '%0.2f',
              previous_slice_fiat_investing
            )
            order_slice[:invest] = format('%0.2f', fiat_investing)
            order_slice[:return] = format('%0.2f', fiat_returning)
            order_slice[:profit] = format('%0.2f', profit)
            order_slice[:tpm] = tpm
            order_slice[:autotrade_percent] = autotrade_percent
            order_slice[:color] = plan_color
            order_slice[:last_slice] = last_slice_detected

            order_plan.push(order_slice) unless event_history.recalculate_order_plan
            fiat_budget -= fiat_investing
            previous_slice_fiat_investing = fiat_investing
            plan_no_slice += 1
          end
          event_history.order_book[:order_plan] = order_plan
        end

        red_pill_alert = '| RED PILL ALERT |'
        red_pill_msg = '- Autotrade % Exhausted -'
        event_history.red_pill = true if order_plan.empty?

        if event_history.red_pill
          order_plan_prefix = '--'
          max_order_plan_slices = '0'
          order_plan_volume_out = '0.00'
          order_plan_profit_sum_out = '0.00'
          order_plan_exec_percent = '0.00'
        else
          order_plan_len = order_plan.length
          order_plan_prefix = order_plan.last[:plan_no].split('.').first
          max_order_plan_slices = order_plan.last[:plan_no].split('.').last.to_i
          calc_order_plan_exec = (
            1 - (order_plan_len / max_order_plan_slices.to_f)
          ) * 100
          order_plan_volume = order_plan.map do |op|
            op[:invest].to_f
          end.sum
          order_plan_volume_out = Cryptum.beautify_large_number(
            value: format(
              '%0.2f',
              order_plan_volume
            )
          )
          order_plan_profit_sum = order_plan.map do |op|
            op[:profit].to_f
          end.sum
          order_plan_profit_sum_out = Cryptum.beautify_large_number(
            value: format(
              '%0.2f',
              order_plan_profit_sum
            )
          )
          order_plan_exec_percent = format('%0.2f', calc_order_plan_exec)
        end

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

        # UI
        col_just1 = (Curses.cols - Cryptum::UI.col_first) - 1
        col_just4 = Curses.cols - Cryptum::UI.col_fourth

        # ROW 1
        out_line_no = 0
        line_color = :white
        header_color = :white
        header_style = :bold
        style = :bold
        if event_history.order_plan_win_active
          line_color = :blue
          header_color = :blue
          header_style = :reverse
        end

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

        # ROW 2
        out_line_no += 1
        order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
        order_plan_win.clrtoeol
        Cryptum::UI.colorize(
          ui_win: order_plan_win,
          color: header_color,
          style: header_style,
          string: ''.ljust(col_just1, ' ')
        )

        header_str = "- ORDER PLAN | CYCLE ##{order_plan_prefix} -"
        header_str = '- ORDER PLAN | UNAVAILABLE -' if event_history.red_pill
        order_plan_win.setpos(
          out_line_no,
          Cryptum::UI.col_center(str: header_str)
        )

        Cryptum::UI.colorize(
          ui_win: order_plan_win,
          color: header_color,
          style: header_style,
          string: header_str
        )

        order_plan_win.setpos(out_line_no, Cryptum::UI.col_fourth)
        Cryptum::UI.colorize(
          ui_win: order_plan_win,
          color: header_color,
          style: header_style,
          string: ''.ljust(col_just4, ' ')
        )

        # ROWS 3-10
        remaining_blank_rows = 0
        max_rows_to_display = event_history.order_plan_max_rows_to_display
        remaining_blank_rows = max_rows_to_display if order_plan.empty?
        if event_history.red_pill
          out_line_no += 1
          order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
          order_plan_win.clrtoeol

          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :red,
            style: :highlight,
            string: ''.ljust(col_just1, ' ')
          )

          order_plan_win.setpos(
            out_line_no,
            Cryptum::UI.col_center(str: red_pill_alert)
          )
          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :red,
            style: :highlight,
            string: red_pill_alert
          )

          order_plan_win.setpos(out_line_no, Cryptum::UI.col_fourth)
          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :red,
            style: :highlight,
            string: ''.ljust(col_just4, ' ')
          )

          out_line_no += 1
          order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
          order_plan_win.clrtoeol

          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :yellow,
            string: ''.ljust(col_just1, ' ')
          )

          order_plan_win.setpos(
            out_line_no,
            Cryptum::UI.col_center(str: red_pill_msg)
          )
          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :yellow,
            style: :bold,
            string: red_pill_msg
          )

          order_plan_win.setpos(out_line_no, Cryptum::UI.col_fourth)
          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: :yellow,
            string: ''.ljust(col_just4, ' ')
          )

          max_rows_to_display.times.each do
            out_line_no += 1
            this_matrix_row = Cryptum::Matrix.generate(cols: Curses.cols)
            order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
            order_plan_win.clrtoeol
            Cryptum::UI.colorize(
              ui_win: order_plan_win,
              color: :red,
              style: :bold,
              string: this_matrix_row
            )
          end
        else
          market_trend_color = indicator_status.market_trend[:color] if indicator_status.market_trend
          plan_color = :cyan if market_trend_color == :red
          plan_color = :green if market_trend_color == :green

          # ROWS 3-10
          max_rows_to_display = event_history.order_plan_max_rows_to_display
          first_row = event_history.order_plan_index_offset
          last_row = first_row + max_rows_to_display
          if last_row >= order_plan.length
            last_row = order_plan.length - 1
            event_history.order_plan_max_records_available_to_display = last_row if last_row < max_rows_to_display
            first_row = last_row - event_history.order_plan_max_records_available_to_display
            event_history.order_plan_index_offset = first_row
            remaining_blank_rows = max_rows_to_display - last_row
            remaining_blank_rows = max_rows_to_display - last_row - 1 if last_row < max_rows_to_display
          end

          selected_order = event_history.order_plan_selected_data
          order_plan[first_row..last_row].each do |order|
            out_line_no += 1
            current_line = out_line_no - 2

            style = :normal
            if event_history.order_plan_row_to_select == current_line
              style = :highlight
              selected_order = order
              selected_order[:color] = plan_color
            end

            fiat_avail_out = Cryptum.beautify_large_number(
              value: order[:fiat_available]
            )
            risk_alloc_out = Cryptum.beautify_large_number(
              value: order[:risk_alloc]
            )
            slice_alloc_out = Cryptum.beautify_large_number(
              value: order[:slice_alloc]
            )
            previous_slice_invest_out = Cryptum.beautify_large_number(
              value: order[:previous_slice_invest]
            )
            invest_out = Cryptum.beautify_large_number(
              value: order[:invest]
            )
            profit_out = Cryptum.beautify_large_number(
              value: order[:profit]
            )
            tpm_out = format('%0.2f', tpm)
            return_out = Cryptum.beautify_large_number(
              value: order[:return]
            )
            plan_no = "#{order[:plan_no]}|"
            fiat = "#{autotrade_percent}% of $#{fiat_avail_out} = "
            alloc = "$#{risk_alloc_out} @ #{order[:allocation_percent]}% = "
            alloc = "$#{risk_alloc_out}" if order[:last_slice]
            invest = "$#{slice_alloc_out} + $#{previous_slice_invest_out} = $#{invest_out} + #{tpm_out}% = "
            invest = " + #{tpm_out}% = " if order[:last_slice]

            returns = "$#{return_out}|"
            profit = "Profit: $#{profit_out}"

            order_plan_invest = "#{plan_no}#{fiat}#{alloc}#{invest}"
            order_plan_return = "#{returns}#{profit}"

            order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
            order_plan_win.clrtoeol
            Cryptum::UI.colorize(
              ui_win: order_plan_win,
              color: plan_color,
              style: style,
              string: "#{order_plan_invest}#{order_plan_return}".ljust(col_just1, '.')
            )

            Cryptum::UI.colorize(
              ui_win: order_plan_win,
              color: plan_color,
              style: style,
              string: ''.ljust(col_just4, ' ')
            )
          end
          event_history.order_plan_selected_data = selected_order

          if order_plan.length < 9
            lines_to_clear = 9 - order_plan.length
            (1..lines_to_clear).each do |_line|
              out_line_no += 1
              order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
              order_plan_win.clrtoeol
            end
          end

          # Clear to SUMMARY
          # (Only Applicable if order_book[:order_plan] < max_rows_to_display)
          # out_line_no += 1
          rows_to_blank = (out_line_no + remaining_blank_rows) - 1
          (out_line_no..rows_to_blank).each do |clr_line|
            out_line_no = clr_line
            order_plan_win.setpos(clr_line, Cryptum::UI.col_first)
            order_plan_win.clrtoeol
            Cryptum::UI.colorize(
              ui_win: order_plan_win,
              color: :white,
              style: :normal,
              string: ''.ljust(col_just1, ' ')
            )
          end

          # ROW 10
          out_line_no += 1
          order_plan_win.setpos(out_line_no, Cryptum::UI.col_first)
          order_plan_win.clrtoeol
          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: header_color,
            style: header_style,
            string: ''.ljust(col_just1, ' ')
          )

          header_str = "CYCLE SUMMARY | Slices: #{max_order_plan_slices} | Risk Allocated: $#{order_plan_volume_out} | Profit: $#{order_plan_profit_sum_out} | #{order_plan_exec_percent}% Done"
          order_plan_win.setpos(
            out_line_no,
            Cryptum::UI.col_center(str: header_str)
          )

          Cryptum::UI.colorize(
            ui_win: order_plan_win,
            color: header_color,
            style: header_style,
            string: header_str
          )
        end

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

        order_plan_win.refresh

        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