class ConditionForm
  include ActiveModel::Model
  include Logic

  attr_accessor :top_operator_name, :rows, :source_tdcs

  def to_condition
    case sub_conditions.count
    when 0
      nil
    when 1
      sub_conditions.first
    else
      top_operator_class.new(sub_conditions)
    end
  end

  def delete_row(i)
    rows.slice!(i)

    self
  end

  def change_champ(i)
    sub_conditions[i] = Logic.ensure_compatibility_from_left(sub_conditions[i], source_tdcs)

    self
  end

  private

  def top_operator_class
    Logic.class_from_name(top_operator_name)
  end

  def sub_conditions
    @sub_conditions ||= rows.map { |row| row_to_condition(row) }
  end

  def row_to_condition(row)
    left = Logic.from_json(row[:targeted_champ])
    right = parse_value(left.type(source_tdcs), row[:value])

    Logic.class_from_name(row[:operator_name]).new(left, right)
  end

  def parse_value(left_type, value)
    return empty if value.blank?

    if left_type == :number
      # in this special case, we try to cast as Float, then Integer
      # but it can still be a previous string value or a mistap
      number = parse_to_number(value)

      return constant(number) if number
    end

    # otherwise it can be a serialized Constant(true | false) term
    # or a serialized Empty term
    term = Logic.from_json(value) rescue nil
    return term if term.present?

    # if anything else, save it as a constant of string
    constant(value)
  end

  def parse_to_number(str)
    float = Float(str)
    float % 1 == 0 ? float.to_i : float
  rescue ArgumentError
    nil
  end
end