import pytest

from jinjax import Component, DuplicateDefDeclaration, InvalidArgument


def test_load_args():
    com = Component(
        name="Test.jinja",
        source='{#def message, lorem=4, ipsum="bar" -#}\n',
    )
    assert com.required == ["message"]
    assert com.optional == {
        "lorem": 4,
        "ipsum": "bar",
    }


def test_expression_args():
    com = Component(
        name="Test.jinja",
        source="{#def expr=1 + 2 + 3, a=1 -#}\n",
    )
    assert com.required == []
    assert com.optional == {
        "expr": 6,
        "a": 1,
    }


def test_dict_args():
    com = Component(
        name="Test.jinja",
        source="{#def expr={'a': 'b', 'c': 'd'} -#}\n",
    )
    assert com.optional == {
        "expr": {"a": "b", "c": "d"},
    }

    com = Component(
        name="Test.jinja",
        source='{#def a=1, expr={"a": "b", "c": "d"} -#}\n',
    )
    assert com.optional == {
        "a": 1,
        "expr": {"a": "b", "c": "d"},
    }


def test_lowercase_booleans():
    com = Component(
        name="Test.jinja",
        source="{#def a=false, b=true -#}\n",
    )
    assert com.optional == {
        "a": False,
        "b": True,
    }


def test_no_args():
    com = Component(
        name="Test.jinja",
        source="\n",
    )
    assert com.required == []
    assert com.optional == {}


def test_fails_when_invalid_name():
    with pytest.raises(InvalidArgument):
        source = "{#def 000abc -#}\n"
        co = Component(name="", source=source)
        print(co.required, co.optional)


def test_fails_when_missing_comma_between_args():
    with pytest.raises(InvalidArgument):
        source = "{#def lorem ipsum -#}\n"
        co = Component(name="", source=source)
        print(co.required, co.optional)


def test_fails_when_missing_quotes_arround_default_value():
    with pytest.raises(InvalidArgument):
        source = "{#def lorem=ipsum -#}\n"
        co = Component(name="", source=source)
        print(co.required, co.optional)


def test_fails_when_prop_is_expression():
    with pytest.raises(InvalidArgument):
        source = "{#def a-b -#}\n"
        co = Component(name="", source=source)
        print(co.required, co.optional)


def test_fails_when_extra_comma_between_args():
    with pytest.raises(InvalidArgument):
        source = "{#def a, , b -#}\n"
        co = Component(name="", source=source)
        print(co.required, co.optional)


def test_comma_in_default_value():
    com = Component(
        name="Test.jinja",
        source="{#def a='lorem, ipsum' -#}\n",
    )
    assert com.optional == {"a": "lorem, ipsum"}


def test_load_assets():
    com = Component(
        name="Test.jinja",
        url_prefix="/static/",
        source="""
        {#css a.css, "b.css", c.css -#}
        {#js a.js, b.js, c.js -#}
        """,
    )
    assert com.css == ["/static/a.css", "/static/b.css", "/static/c.css"]
    assert com.js == ["/static/a.js", "/static/b.js", "/static/c.js"]


def test_no_comma_in_assets_list_is_your_problem():
    com = Component(
        name="Test.jinja",
        source="{#js a.js b.js c.js -#}\n",
        url_prefix="/static/"
    )
    assert com.js == ["/static/a.js b.js c.js"]


def test_load_metadata_in_any_order():
    com = Component(
        name="Test.jinja",
        source="""
        {#css a.css #}
        {#def lorem, ipsum=4 #}
        {#js a.js #}
        """,
    )
    assert com.required == ["lorem"]
    assert com.optional == {"ipsum": 4}
    assert com.css == ["a.css"]
    assert com.js == ["a.js"]


def test_ignore_metadata_if_not_first():
    com = Component(
        name="Test.jinja",
        source="""
        I am content
        {#css a.css #}
        {#def lorem, ipsum=4 #}
        {#js a.js #}
        """,
    )
    assert com.required == []
    assert com.optional == {}
    assert com.css == []
    assert com.js == []


def test_fail_with_more_than_one_args_declaration():
    with pytest.raises(DuplicateDefDeclaration):
        Component(
            name="Test.jinja",
            source="""
            {#def lorem, ipsum=4 #}
            {#def a, b, c, ipsum="nope" #}
            """,
        )


def test_merge_repeated_css_or_js_declarations():
    com = Component(
        name="Test.jinja",
        source="""
        {#css a.css #}
        {#def lorem, ipsum=4 #}
        {#css b.css #}
        {#js a.js #}
        {#js b.js #}
        """,
    )
    assert com.required == ["lorem"]
    assert com.optional == {"ipsum": 4}
    assert com.css == ["a.css", "b.css"]
    assert com.js == ["a.js", "b.js"]


def test_linejump_in_args_decl():
    com = Component(
        name="Test.jinja",
        source='{#def\n  message,\n  lorem=4,\n  ipsum="bar"\n#}\n',
    )
    assert com.required == ["message"]
    assert com.optional == {
        "lorem": 4,
        "ipsum": "bar",
    }


def test_global_assets():
    com = Component(
        name="Test.jinja",
        source="""
        {#css a.css, /static/shared/b.css, http://example.com/cdn.css #}
        {#js "http://example.com/cdn.js", a.js, /static/shared/b.js #}
        """,
    )
    assert com.css == ["a.css", "/static/shared/b.css", "http://example.com/cdn.css"]
    assert com.js == ["http://example.com/cdn.js", "a.js", "/static/shared/b.js"]


def test_types_in_args_decl():
    com = Component(
        name="Test.jinja",
        source="""{# def
  ring_class: str = "ring-1 ring-black",
  rounded_class: str = "rounded-2xl md:rounded-3xl",

  image: str | None = None,

  title: str = "",
  p_class: str = "px-5 md:px-6 py-5 md:py-6",
  gap_class: str = "gap-4",
  content_class: str = "",

  layer_class: str | None = None,
  layer_height: int = 4,
#}"""
    )
    assert com.required == []
    print(com.optional)
    assert com.optional == {
        "ring_class": "ring-1 ring-black",
        "rounded_class": "rounded-2xl md:rounded-3xl",
        "image": None,
        "title": "",
        "p_class": "px-5 md:px-6 py-5 md:py-6",
        "gap_class": "gap-4",
        "content_class": "",
        "layer_class": None,
        "layer_height": 4,
    }


def test_comments_in_args_decl():
    com = Component(
        name="Test.jinja",
        source="""{# def
  #
  # Card style
  ring_class: str = "ring-1 ring-black",
  rounded_class: str = "rounded-2xl md:rounded-3xl",
  #
  # Image
  image: str | None = None,
  #
  # Content
  title: str = "",
  p_class: str = "px-5 md:px-6 py-5 md:py-6",
  gap_class: str = "gap-4",
  content_class: str = "",
  #
  # Decorative layer
  layer_class: str | None = None,
  layer_height: int = 4,
#}"""
    )
    assert com.required == []
    print(com.optional)
    assert com.optional == {
        "ring_class": "ring-1 ring-black",
        "rounded_class": "rounded-2xl md:rounded-3xl",
        "image": None,
        "title": "",
        "p_class": "px-5 md:px-6 py-5 md:py-6",
        "gap_class": "gap-4",
        "content_class": "",
        "layer_class": None,
        "layer_height": 4,
    }


def test_comment_after_args_decl():
    com = Component(
        name="Test.jinja",
        source="""
{# def
  arg,
#}

{#
  Some comment.
#}
Hi
""".strip())
    assert com.required == ["arg"]
    assert com.optional == {}


def test_fake_decl():
    com = Component(
        name="Test.jinja",
        source="""
{# definitely not an args decl! #}
{# def arg #}
{# jsadfghkl are letters #}
{# csssssss #}
""".strip())
    assert com.required == ["arg"]
    assert com.optional == {}