# frozen_string_literal: true

require 'test_helper'

class ParseTreeVisitorTest < Minitest::Test
  include Liquid

  def test_variable
    assert_equal(
      ["test"],
      visit(%({{ test }})),
    )
  end

  def test_varible_with_filter
    assert_equal(
      ["test", "infilter"],
      visit(%({{ test | split: infilter }})),
    )
  end

  def test_dynamic_variable
    assert_equal(
      ["test", "inlookup"],
      visit(%({{ test[inlookup] }})),
    )
  end

  def test_echo
    assert_equal(
      ["test"],
      visit(%({% echo test %})),
    )
  end

  def test_if_condition
    assert_equal(
      ["test"],
      visit(%({% if test %}{% endif %})),
    )
  end

  def test_complex_if_condition
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 and 2 == test %}{% endif %})),
    )
  end

  def test_if_body
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 %}{{ test }}{% endif %})),
    )
  end

  def test_unless_condition
    assert_equal(
      ["test"],
      visit(%({% unless test %}{% endunless %})),
    )
  end

  def test_complex_unless_condition
    assert_equal(
      ["test"],
      visit(%({% unless 1 == 1 and 2 == test %}{% endunless %})),
    )
  end

  def test_unless_body
    assert_equal(
      ["test"],
      visit(%({% unless 1 == 1 %}{{ test }}{% endunless %})),
    )
  end

  def test_elsif_condition
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 %}{% elsif test %}{% endif %})),
    )
  end

  def test_complex_elsif_condition
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 %}{% elsif 1 == 1 and 2 == test %}{% endif %})),
    )
  end

  def test_elsif_body
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 %}{% elsif 2 == 2 %}{{ test }}{% endif %})),
    )
  end

  def test_else_body
    assert_equal(
      ["test"],
      visit(%({% if 1 == 1 %}{% else %}{{ test }}{% endif %})),
    )
  end

  def test_case_left
    assert_equal(
      ["test"],
      visit(%({% case test %}{% endcase %})),
    )
  end

  def test_case_condition
    assert_equal(
      ["test"],
      visit(%({% case 1 %}{% when test %}{% endcase %})),
    )
  end

  def test_case_when_body
    assert_equal(
      ["test"],
      visit(%({% case 1 %}{% when 2 %}{{ test }}{% endcase %})),
    )
  end

  def test_case_else_body
    assert_equal(
      ["test"],
      visit(%({% case 1 %}{% else %}{{ test }}{% endcase %})),
    )
  end

  def test_for_in
    assert_equal(
      ["test"],
      visit(%({% for x in test %}{% endfor %})),
    )
  end

  def test_for_limit
    assert_equal(
      ["test"],
      visit(%({% for x in (1..5) limit: test %}{% endfor %})),
    )
  end

  def test_for_offset
    assert_equal(
      ["test"],
      visit(%({% for x in (1..5) offset: test %}{% endfor %})),
    )
  end

  def test_for_body
    assert_equal(
      ["test"],
      visit(%({% for x in (1..5) %}{{ test }}{% endfor %})),
    )
  end

  def test_for_range
    assert_equal(
      ["test"],
      visit(%({% for x in (1..test) %}{% endfor %})),
    )
  end

  def test_tablerow_in
    assert_equal(
      ["test"],
      visit(%({% tablerow x in test %}{% endtablerow %})),
    )
  end

  def test_tablerow_limit
    assert_equal(
      ["test"],
      visit(%({% tablerow x in (1..5) limit: test %}{% endtablerow %})),
    )
  end

  def test_tablerow_offset
    assert_equal(
      ["test"],
      visit(%({% tablerow x in (1..5) offset: test %}{% endtablerow %})),
    )
  end

  def test_tablerow_body
    assert_equal(
      ["test"],
      visit(%({% tablerow x in (1..5) %}{{ test }}{% endtablerow %})),
    )
  end

  def test_cycle
    assert_equal(
      ["test"],
      visit(%({% cycle test %})),
    )
  end

  def test_assign
    assert_equal(
      ["test"],
      visit(%({% assign x = test %})),
    )
  end

  def test_capture
    assert_equal(
      ["test"],
      visit(%({% capture x %}{{ test }}{% endcapture %})),
    )
  end

  def test_include
    assert_equal(
      ["test"],
      visit(%({% include test %})),
    )
  end

  def test_include_with
    assert_equal(
      ["test"],
      visit(%({% include "hai" with test %})),
    )
  end

  def test_include_for
    assert_equal(
      ["test"],
      visit(%({% include "hai" for test %})),
    )
  end

  def test_render_with
    assert_equal(
      ["test"],
      visit(%({% render "hai" with test %})),
    )
  end

  def test_render_for
    assert_equal(
      ["test"],
      visit(%({% render "hai" for test %})),
    )
  end

  def test_preserve_tree_structure
    assert_equal(
      [[nil, [
        [nil, [[nil, [["other", []]]]]],
        ["test", []],
        ["xs", []],
      ]]],
      traversal(%({% for x in xs offset: test %}{{ other }}{% endfor %})).visit,
    )
  end

  private

  def traversal(template)
    ParseTreeVisitor
      .for(Template.parse(template).root)
      .add_callback_for(VariableLookup) { |node| node.name } # rubocop:disable Style/SymbolProc
  end

  def visit(template)
    traversal(template).visit.flatten.compact
  end
end
