# ruby
require_relative 'runtime_name_ht'
require_relative 'runtime_name_handler'
# frozen_string_literal: true

require_relative 'command_type'

module Hypertube
  module Utils
    class Command
      attr_reader :runtime_name, :command_type, :payload

      def initialize(runtime_name, command_type, *payload)
        @runtime_name = runtime_name
        @command_type = command_type

        @payload = if payload.empty?
                     []
                   elsif payload.size == 1 && payload[0].is_a?(Array)
                     # caller passed a single array -> reuse it (no extra copy)
                     payload[0] || []
                   else
                     # varargs or multiple args -> use as provided
                     payload
                   end
      end

      def self.create_response(response, runtime_name)
        Hypertube::Utils::Command.new(runtime_name, Hypertube::Utils::CommandType::VALUE, response)
      end

      def self.create_reference(guid, runtime_name)
        Hypertube::Utils::Command.new(runtime_name, Hypertube::Utils::CommandType::REFERENCE, guid)
      end

      def drop_first_payload_argument
        return Hypertube::Utils::Command.new(@runtime_name, @command_type, []) if @payload.length <= 1

        new_payload = @payload[1..-1] || []
        Hypertube::Utils::Command.new(@runtime_name, @command_type, new_payload)
      end

      def add_arg_to_payload(argument)
        new_payload = @payload + [argument]
        Hypertube::Utils::Command.new(@runtime_name, @command_type, new_payload)
      end

      def prepend_arg_to_payload(arg_command)
        return self if arg_command.nil?

        new_payload = [arg_command] + @payload
        Hypertube::Utils::Command.new(@runtime_name, @command_type, new_payload)
      end

      def to_s
        result = '  { '
        result += "RuntimeName: #{@runtime_name}, "
        result += "Hypertube::Utils::CommandType: #{@command_type}, "
        result += "Payload: \n"
        result += '    ['

        payload = @payload
        len = payload.length

        if len > 0
          result += "\n"
          len.times do |i|
            item = payload[i]
            item_str = if item.nil?
                         'null'
                       elsif item.is_a?(String)
                         "  \"#{item}\""
                       elsif item.is_a?(Hypertube::Utils::Command)
                         item.to_string.gsub("\n", "\n    ")
                       else
                         "  #{item}"
                       end

            result += "    #{item_str}"
            result += i < len - 1 ? ",\n" : "\n"
          end
          result += "    ]\n"
        else
          result += "  ]\n"
        end

        result += '  }'
        result
      rescue StandardError => e
        "Error while converting command to string:#{e.message}"
      end

      def to_string
        to_s
      end

      def eql?(other)
        return true if equal?(other)
        return false unless other.is_a?(Hypertube::Utils::Command)
        return false unless runtime_name == other.runtime_name && command_type == other.command_type
        return false unless payload.length == other.payload.length

        payload.each_with_index do |item, i|
          return false unless item.eql?(other.payload[i])
        end

        true
      end

      alias == eql?
    end
  end
end
