require 'test_helper'

class SablonCustomFieldHandlerTest < Sablon::TestCase
  #
  # This class supports more advanced conditional expressions and crafted
  # by @moritzgloeckl in PR #73
  class OperatorCondition < Sablon::Statement::Condition
    def eval_conditional_blocks(env)
      #
      # evaluate each expression until a true one is found, false blocks
      # are cleared from the document.
      until @conditions.empty?
        condition = @conditions.shift
        conditon_expr = condition[:condition_expr]
        predicate = condition[:predicate]
        block = condition[:block]
        #
        # determine valeu of conditional expression + predicate
        value = eval_condition_expr(conditon_expr, predicate, env.context)
        #
        # manipulate block based on truthy-ness of value
        if truthy?(value)
          block.replace(block.process(env).reverse)
          break true
        else
          block.replace([])
        end
      end
    end

    def eval_condition_expr(conditon_expr, predicate, context)
      value = conditon_expr.evaluate(context)
      #
      if predicate.to_s =~ /^[!=]=/
        operator = predicate[0..1]
        cmpr_val = predicate[2..-1].tr("'", '')
        compare_values(value.to_s, cmpr_val, operator)
      elsif predicate
        value.public_send(predicate)
      else
        value
      end
    end

    def compare_values(value_a, value_b, operator)
      case operator
      when '!='
        value_a != value_b
      when '=='
        value_a == value_b
      end
    end
  end

  # Handles conditional blocks in the template that use an operator
  class OperatorConditionalHandler < Sablon::Processor::Document::ConditionalHandler
    def build_statement(constructor, field, _options = {})
      expr_name = field.expression.match(@pattern).to_a[1]
      args = [
        # end expression (first arg)
        "#{expr_name}:endIf",
        # sub block patterns to check for
        /(#{expr_name}):els[iI]f(?:\(([^)]+)\))?/,
        /(#{expr_name}):else/
      ]
      blocks = process_blocks(constructor.consume_multi_block(*args))
      OperatorCondition.new(blocks)
    end
  end

  def setup
    super
    @base_path = Pathname.new(File.expand_path('../', __FILE__))
    @template_path = @base_path + 'fixtures/custom_field_handlers_template.docx'
    @output_path = @base_path + 'sandbox/custom_field_handlers.docx'
    @sample_path = @base_path + 'fixtures/custom_field_handlers_sample.docx'
    #
    # register new handlers to allow insertion without equals sign and
    # advanced conditionals
    klass = Sablon::Processor::Document
    @orig_conditional_handler = klass.remove_field_handler :conditional
    klass.register_field_handler :default, klass.field_handlers[:insertion]
    klass.register_field_handler :conditional, OperatorConditionalHandler.new
  end

  def teardown
    # remove extra handlers
    Sablon::Processor::Document.remove_field_handler :default
    Sablon::Processor::Document.replace_field_handler :conditional, @orig_conditional_handler
  end

  def test_generate_document_from_template
    template = Sablon.template @template_path
    context = {
      normal_field: 'success1',
      no_leading_equals: 'success2',
      inside_if_no_op: 'success3',
      no_operator: OpenStruct.new(test: 'success4'),
      equals_operator: 'test',
      inside_if_equals_op: 'success5'
    }
    #
    template.render_to_file @output_path, context
    assert_docx_equal @sample_path, @output_path
  end
end