# coding: utf-8

"""
comment testing is all about roundtrips
these can be done in the "old" way by creating a file.data and file.roundtrip
but there is little flexibility in doing that

but some things are not easily tested, eog. how a
roundtrip changes

"""

import sys

import pytest  # type: ignore  # NOQA
from roundtrip import (  # type: ignore
    dedent,
    round_trip,
    round_trip_dump,
    round_trip_load,
)


class TestComments:
    def test_no_end_of_file_eol(self) -> None:
        """not excluding comments caused some problems if at the end of
        the file without a newline. First error, then included \0"""
        x = """\
        - europe: 10 # abc"""
        round_trip(x, extra='\n')
        with pytest.raises(AssertionError):
            round_trip(x, extra='a\n')

    def test_no_comments(self) -> None:
        round_trip(
            """
        - europe: 10
        - usa:
          - ohio: 2
          - california: 9
        """
        )

    def test_round_trip_ordering(self) -> None:
        round_trip(
            """
        a: 1
        b: 2
        c: 3
        b1: 2
        b2: 2
        d: 4
        e: 5
        f: 6
        """
        )

    def test_complex(self) -> None:
        round_trip(
            """
        - europe: 10 # top
        - usa:
          - ohio: 2
          - california: 9 # o
        """
        )

    def test_dropped(self) -> None:
        s = """\
        # comment
        scalar
        ...
        """
        round_trip(s, 'scalar\n...\n')

    def test_main_mapping_begin_end(self) -> None:
        round_trip(
            """
        # C start a
        # C start b
        abc: 1
        ghi: 2
        klm: 3
        # C end a
        # C end b
        """
        )

    def test_reindent(self) -> None:
        x = """\
        a:
          b:     # comment 1
            c: 1 # comment 2
        """
        d = round_trip_load(x)
        y = round_trip_dump(d, indent=4)
        assert y == dedent(
            """\
        a:
            b:   # comment 1
                c: 1 # comment 2
        """
        )

    def test_main_mapping_begin_end_items_post(self) -> None:
        round_trip(
            """
        # C start a
        # C start b
        abc: 1      # abc comment
        ghi: 2
        klm: 3      # klm comment
        # C end a
        # C end b
        """
        )

    def test_main_sequence_begin_end(self) -> None:
        round_trip(
            """
        # C start a
        # C start b
        - abc
        - ghi
        - klm
        # C end a
        # C end b
        """
        )

    def test_main_sequence_begin_end_items_post(self) -> None:
        round_trip(
            """
        # C start a
        # C start b
        - abc      # abc comment
        - ghi
        - klm      # klm comment
        # C end a
        # C end b
        """
        )

    def test_main_mapping_begin_end_complex(self) -> None:
        round_trip(
            """
        # C start a
        # C start b
        abc: 1
        ghi: 2
        klm:
          3a: alpha
          3b: beta   # it is all greek to me
        # C end a
        # C end b
        """
        )

    def test_09(self) -> None:  # 2.9 from the examples in the spec
        s = """\
        hr: # 1998 hr ranking
          - Mark McGwire
          - Sammy Sosa
        rbi:
          # 1998 rbi ranking
          - Sammy Sosa
          - Ken Griffey
        """
        round_trip(s, indent=4, block_seq_indent=2)

    def test_09a(self) -> None:
        round_trip(
            """
        hr: # 1998 hr ranking
        - Mark McGwire
        - Sammy Sosa
        rbi:
          # 1998 rbi ranking
        - Sammy Sosa
        - Ken Griffey
        """
        )

    def test_simple_map_middle_comment(self) -> None:
        round_trip(
            """
        abc: 1
        # C 3a
        # C 3b
        ghi: 2
        """
        )

    def test_map_in_map_0(self) -> None:
        round_trip(
            """
        map1: # comment 1
          # comment 2
          map2:
            key1: val1
        """
        )

    def test_map_in_map_1(self) -> None:
        # comment is moved from value to key
        round_trip(
            """
        map1:
          # comment 1
          map2:
            key1: val1
        """
        )

    def test_application_arguments(self) -> None:
        # application configur
        round_trip(
            """
        args:
          username: anthon
          passwd: secret
          fullname: Anthon van der Neut
          tmux:
            session-name: test
          loop:
            wait: 10
        """
        )

    def test_substitute(self) -> None:
        x = """
        args:
          username: anthon          # name
          passwd: secret            # password
          fullname: Anthon van der Neut
          tmux:
            session-name: test
          loop:
            wait: 10
        """
        data = round_trip_load(x)
        data['args']['passwd'] = 'deleted password'
        # note the requirement to add spaces for alignment of comment
        x = x.replace(': secret          ', ': deleted password')
        assert round_trip_dump(data) == dedent(x)

    def test_set_comment(self) -> None:
        round_trip(
            """
        !!set
        # the beginning
        ? a
        # next one is B (lowercase)
        ? b  #  You see? Promised you.
        ? c
        # this is the end
        """
        )

    def test_omap_comment_roundtrip(self) -> None:
        round_trip(
            """
        !!omap
        - a: 1
        - b: 2  # two
        - c: 3  # three
        - d: 4
        """
        )

    def test_omap_comment_roundtrip_pre_comment(self) -> None:
        round_trip(
            """
        !!omap
        - a: 1
        - b: 2  # two
        - c: 3  # three
        # last one
        - d: 4
        """
        )

    def test_non_ascii(self) -> None:
        round_trip(
            """
        verbosity: 1                  # 0 is minimal output, -1 none
        base_url: http://gopher.net
        special_indices: [1, 5, 8]
        also_special:
        - a
        - 19
        - 32
        asia and europe: &asia_europe
          Turkey: Ankara
          Russia: Moscow
        countries:
          Asia:
            <<: *asia_europe
            Japan: Tokyo # 東京
          Europe:
            <<: *asia_europe
            Spain: Madrid
            Italy: Rome
        """
        )

    def test_dump_utf8(self) -> None:
        import ruyaml  # NOQA

        x = dedent(
            """\
        ab:
        - x  # comment
        - y  # more comment
        """
        )
        data = round_trip_load(x)
        for utf in [True, False]:
            y = round_trip_dump(
                data,
                default_flow_style=False,
                allow_unicode=utf,
            )
            assert y == x

    def test_dump_unicode_utf8(self) -> None:
        import ruyaml  # NOQA

        x = dedent(
            """\
        ab:
        - x  # comment
        - y  # more comment
        """
        )
        data = round_trip_load(x)
        for utf in [True, False]:
            y = round_trip_dump(
                data,
                default_flow_style=False,
                allow_unicode=utf,
            )
            assert y == x

    def test_mlget_00(self) -> None:
        x = """\
        a:
        - b:
          c: 42
        - d:
            f: 196
          e:
            g: 3.14
        """
        d = round_trip_load(x)
        assert d.mlget(['a', 1, 'd', 'f'], list_ok=True) == 196
        # with pytest.raises(AssertionError):
        #     d.mlget(['a', 1, 'd', 'f']) == 196


class TestInsertPopList:
    """list insertion is more complex than dict insertion, as you
    need to move the values to subsequent keys on insert"""

    @property
    def ins(self) -> str:
        return """\
        ab:
        - a      # a
        - b      # b
        - c
        - d      # d

        de:
        - 1
        - 2
        """

    def test_insert_0(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].insert(0, 'xyz')
        y = round_trip_dump(d, indent=2)
        assert y == dedent(
            """\
        ab:
        - xyz
        - a      # a
        - b      # b
        - c
        - d      # d

        de:
        - 1
        - 2
        """
        )

    def test_insert_1(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].insert(4, 'xyz')
        y = round_trip_dump(d, indent=2)
        assert y == dedent(
            """\
        ab:
        - a      # a
        - b      # b
        - c
        - d      # d

        - xyz
        de:
        - 1
        - 2
        """
        )

    def test_insert_2(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].insert(1, 'xyz')
        y = round_trip_dump(d, indent=2)
        assert y == dedent(
            """\
        ab:
        - a      # a
        - xyz
        - b      # b
        - c
        - d      # d

        de:
        - 1
        - 2
        """
        )

    def test_pop_0(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].pop(0)
        y = round_trip_dump(d, indent=2)
        print(y)
        assert y == dedent(
            """\
        ab:
        - b      # b
        - c
        - d      # d

        de:
        - 1
        - 2
        """
        )

    def test_pop_1(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].pop(1)
        y = round_trip_dump(d, indent=2)
        print(y)
        assert y == dedent(
            """\
        ab:
        - a      # a
        - c
        - d      # d

        de:
        - 1
        - 2
        """
        )

    def test_pop_2(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].pop(2)
        y = round_trip_dump(d, indent=2)
        print(y)
        assert y == dedent(
            """\
        ab:
        - a      # a
        - b      # b
        - d      # d

        de:
        - 1
        - 2
        """
        )

    def test_pop_3(self) -> None:
        d = round_trip_load(self.ins)
        d['ab'].pop(3)
        y = round_trip_dump(d, indent=2)
        print(y)
        assert y == dedent(
            """\
        ab:
        - a      # a
        - b      # b
        - c
        de:
        - 1
        - 2
        """
        )


# inspired by demux' question on stackoverflow
# http://stackoverflow.com/a/36970608/1307905
class TestInsertInMapping:
    @property
    def ins(self) -> str:
        return """\
        first_name: Art
        occupation: Architect  # This is an occupation comment
        about: Art Vandelay is a fictional character that George invents...
        """

    def test_insert_at_pos_1(self) -> None:
        d = round_trip_load(self.ins)
        d.insert(1, 'last name', 'Vandelay', comment='new key')
        y = round_trip_dump(d)
        print(y)
        assert y == dedent(
            """\
        first_name: Art
        last name: Vandelay    # new key
        occupation: Architect  # This is an occupation comment
        about: Art Vandelay is a fictional character that George invents...
        """
        )

    def test_insert_at_pos_0(self) -> None:
        d = round_trip_load(self.ins)
        d.insert(0, 'last name', 'Vandelay', comment='new key')
        y = round_trip_dump(d)
        print(y)
        assert y == dedent(
            """\
        last name: Vandelay  # new key
        first_name: Art
        occupation: Architect  # This is an occupation comment
        about: Art Vandelay is a fictional character that George invents...
        """
        )

    def test_insert_at_pos_3(self) -> None:
        # much more simple if done with appending.
        d = round_trip_load(self.ins)
        d.insert(3, 'last name', 'Vandelay', comment='new key')
        y = round_trip_dump(d)
        print(y)
        assert y == dedent(
            """\
        first_name: Art
        occupation: Architect  # This is an occupation comment
        about: Art Vandelay is a fictional character that George invents...
        last name: Vandelay  # new key
        """
        )


class TestCommentedMapMerge:
    def test_in_operator(self) -> None:
        data = round_trip_load(
            """
        x: &base
          a: 1
          b: 2
          c: 3
        y:
          <<: *base
          k: 4
          l: 5
        """
        )
        assert data['x']['a'] == 1
        assert 'a' in data['x']
        assert data['y']['a'] == 1
        assert 'a' in data['y']

    def test_issue_60(self) -> None:
        data = round_trip_load(
            """
        x: &base
          a: 1
        y:
          <<: *base
        """
        )
        assert data['x']['a'] == 1
        assert data['y']['a'] == 1
        assert str(data['y']) == """{'a': 1}"""

    def test_issue_60_1(self) -> None:
        data = round_trip_load(
            """
        x: &base
          a: 1
        y:
          <<: *base
          b: 2
        """
        )
        assert data['x']['a'] == 1
        assert data['y']['a'] == 1
        assert str(data['y']) == """{'b': 2, 'a': 1}"""


class TestEmptyLines:
    # prompted by issue 46 from Alex Harvey
    def test_issue_46(self) -> None:
        yaml_str = dedent(
            """\
        ---
        # Please add key/value pairs in alphabetical order

        aws_s3_bucket: 'mys3bucket'

        jenkins_ad_credentials:
          bind_name: 'CN=svc-AAA-BBB-T,OU=Example,DC=COM,DC=EXAMPLE,DC=Local'
          bind_pass: 'xxxxyyyy{'
        """
        )
        d = round_trip_load(yaml_str, preserve_quotes=True)
        y = round_trip_dump(d, explicit_start=True)
        assert yaml_str == y

    def test_multispace_map(self) -> None:
        round_trip(
            """
        a: 1x

        b: 2x


        c: 3x



        d: 4x

        """
        )

    @pytest.mark.xfail(strict=True)  # type: ignore
    def test_multispace_map_initial(self) -> None:
        round_trip(
            """

        a: 1x

        b: 2x


        c: 3x



        d: 4x

        """
        )

    def test_embedded_map(self) -> None:
        round_trip(
            """
        - a: 1y
          b: 2y

          c: 3y
        """
        )

    def test_toplevel_seq(self) -> None:
        round_trip(
            """\
        - 1

        - 2

        - 3
        """
        )

    def test_embedded_seq(self) -> None:
        round_trip(
            """
        a:
          b:
          - 1

          - 2


          - 3
        """
        )

    def test_line_with_only_spaces(self) -> None:
        # issue 54
        yaml_str = "---\n\na: 'x'\n \nb: y\n"
        d = round_trip_load(yaml_str, preserve_quotes=True)
        y = round_trip_dump(d, explicit_start=True)
        stripped = ""
        for line in yaml_str.splitlines():
            stripped += line.rstrip() + '\n'
            print(line + '$')
        assert stripped == y

    def test_some_eol_spaces(self) -> None:
        # spaces after tokens and on empty lines
        yaml_str = '---  \n  \na: "x"  \n   \nb: y  \n'
        d = round_trip_load(yaml_str, preserve_quotes=True)
        y = round_trip_dump(d, explicit_start=True)
        stripped = ""
        for line in yaml_str.splitlines():
            stripped += line.rstrip() + '\n'
            print(line + '$')
        assert stripped == y

    def test_issue_54_not_ok(self) -> None:
        yaml_str = dedent(
            """\
        toplevel:

            # some comment
            sublevel: 300
        """
        )
        d = round_trip_load(yaml_str)
        print(d.ca)
        y = round_trip_dump(d, indent=4)
        assert isinstance(y, str)
        print(y.replace('\n', '$\n'))
        assert yaml_str == y

    def test_issue_54_ok(self) -> None:
        yaml_str = dedent(
            """\
        toplevel:
            # some comment
            sublevel: 300
        """
        )
        d = round_trip_load(yaml_str)
        y = round_trip_dump(d, indent=4)
        assert yaml_str == y

    def test_issue_93(self) -> None:
        round_trip(
            """\
        a:
          b:
          - c1: cat  # a1
          # my comment on catfish
          - c2: catfish  # a2
        """
        )

    def test_issue_93_00(self) -> None:
        round_trip(
            """\
        a:
        - - c1: cat   # a1
          # my comment on catfish
          - c2: catfish  # a2
        """
        )

    def test_issue_93_01(self) -> None:
        round_trip(
            """\
        - - c1: cat   # a1
          # my comment on catfish
          - c2: catfish  # a2
        """
        )

    def test_issue_93_02(self) -> None:
        # never failed as there is no indent
        round_trip(
            """\
        - c1: cat
        # my comment on catfish
        - c2: catfish
        """
        )

    def test_issue_96(self) -> None:
        # inserted extra line on trailing spaces
        round_trip(
            """\
        a:
          b:
            c: c_val
            d:

          e:
            g: g_val
        """
        )


class TestUnicodeComments:
    @pytest.mark.skipif(sys.version_info < (2, 7), reason='wide unicode')  # type: ignore
    def test_issue_55(self) -> None:  # reported by Haraguroicha Hsu
        round_trip(
            """\
        name: TEST
        description: test using
        author: Harguroicha
        sql:
          command: |-
            select name from testtbl where no = :no

          ci-test:
          - :no: 04043709 # 小花
          - :no: 05161690 # 茶
          - :no: 05293147 # 〇𤋥川
          - :no: 05338777 # 〇〇啓
          - :no: 05273867 # 〇
          - :no: 05205786 # 〇𤦌
        """
        )


class TestEmptyValueBeforeComments:
    def test_issue_25a(self) -> None:
        round_trip(
            """\
        - a: b
          c: d
          d:  # foo
          - e: f
        """
        )

    def test_issue_25a1(self) -> None:
        round_trip(
            """\
        - a: b
          c: d
          d:  # foo
            e: f
        """
        )

    def test_issue_25b(self) -> None:
        round_trip(
            """\
        var1: #empty
        var2: something #notempty
        """
        )

    def test_issue_25c(self) -> None:
        round_trip(
            """\
        params:
          a: 1 # comment a
          b:   # comment b
          c: 3 # comment c
        """
        )

    def test_issue_25c1(self) -> None:
        round_trip(
            """\
        params:
          a: 1 # comment a
          b:   # comment b
          # extra
          c: 3 # comment c
        """
        )

    def test_issue_25_00(self) -> None:
        round_trip(
            """\
        params:
          a: 1 # comment a
          b:   # comment b
        """
        )

    def test_issue_25_01(self) -> None:
        round_trip(
            """\
        a:        # comment 1
                  #  comment 2
        - b:      #   comment 3
            c: 1  #    comment 4
        """
        )

    def test_issue_25_02(self) -> None:
        round_trip(
            """\
        a:        # comment 1
                  #  comment 2
        - b: 2    #   comment 3
        """
        )

    def test_issue_25_03(self) -> None:
        s = """\
        a:        # comment 1
                  #  comment 2
          - b: 2  #   comment 3
        """
        round_trip(s, indent=4, block_seq_indent=2)

    def test_issue_25_04(self) -> None:
        round_trip(
            """\
        a:        # comment 1
                  #  comment 2
          b: 1    #   comment 3
        """
        )

    def test_flow_seq_within_seq(self) -> None:
        round_trip(
            """\
        # comment 1
        - a
        - b
        # comment 2
        - c
        - d
        # comment 3
        - [e]
        - f
        # comment 4
        - []
        """
        )

    def test_comment_after_block_scalar_indicator(self) -> None:
        round_trip(
            """\
        a: | # abc
          test 1
          test 2
        # all done
        """
        )


test_block_scalar_commented_line_template = """\
y: p
# Some comment

a: |
  x
{}b: y
"""


class TestBlockScalarWithComments:
    # issue 99 reported by Colm O'Connor
    def test_scalar_with_comments(self) -> None:
        import ruyaml  # NOQA

        for x in [
            "",
            '\n',
            '\n# Another comment\n',
            '\n\n',
            '\n\n# abc\n#xyz\n',
            '\n\n# abc\n#xyz\n',
            '# abc\n\n#xyz\n',
            '\n\n  # abc\n  #xyz\n',
        ]:
            commented_line = test_block_scalar_commented_line_template.format(x)
            data = round_trip_load(commented_line)

            assert round_trip_dump(data) == commented_line
