# coding: utf-8

from typing import Any

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


def load(s: str) -> Any:
    return round_trip_load(dedent(s))


def compare(data: Any, s: str, **kw: Any) -> None:
    assert round_trip_dump(data, **kw) == dedent(s)


def compare_eol(data: Any, s: str) -> None:
    assert 'EOL' in s
    ds = dedent(s).replace('EOL', '').replace('\n', '|\n')
    assert round_trip_dump(data).replace('\n', '|\n') == ds


class TestCommentsManipulation:
    # list
    def test_seq_set_comment_on_existing_explicit_column(self) -> None:
        data = load(
            """
        - a   # comment 1
        - b
        - c
        """
        )
        data.yaml_add_eol_comment('comment 2', key=1, column=6)
        exp = """
        - a   # comment 1
        - b   # comment 2
        - c
        """
        compare(data, exp)

    def test_seq_overwrite_comment_on_existing_explicit_column(self) -> None:
        data = load(
            """
        - a   # comment 1
        - b
        - c
        """
        )
        data.yaml_add_eol_comment('comment 2', key=0, column=6)
        exp = """
        - a   # comment 2
        - b
        - c
        """
        compare(data, exp)

    def test_seq_first_comment_explicit_column(self) -> None:
        data = load(
            """
        - a
        - b
        - c
        """
        )
        data.yaml_add_eol_comment('comment 1', key=1, column=6)
        exp = """
        - a
        - b   # comment 1
        - c
        """
        compare(data, exp)

    def test_seq_set_comment_on_existing_column_prev(self) -> None:
        data = load(
            """
        - a   # comment 1
        - b
        - c
        - d     # comment 3
        """
        )
        data.yaml_add_eol_comment('comment 2', key=1)
        exp = """
        - a   # comment 1
        - b   # comment 2
        - c
        - d     # comment 3
        """
        compare(data, exp)

    def test_seq_set_comment_on_existing_column_next(self) -> None:
        data = load(
            """
        - a   # comment 1
        - b
        - c
        - d     # comment 3
        """
        )
        print(data.ca)
        # print(type(data._yaml_comment._items[0][0].start_mark))
        # ruyaml.error.Mark
        # print(type(data._yaml_comment._items[0][0].start_mark))
        data.yaml_add_eol_comment('comment 2', key=2)
        exp = """
        - a   # comment 1
        - b
        - c     # comment 2
        - d     # comment 3
        """
        compare(data, exp)

    def test_seq_set_comment_on_existing_column_further_away(self) -> None:
        """
        no comment line before or after, take the latest before
        the new position
        """
        data = load(
            """
        - a   # comment 1
        - b
        - c
        - d
        - e
        - f     # comment 3
        """
        )
        print(data.ca)
        # print(type(data._yaml_comment._items[0][0].start_mark))
        # ruyaml.error.Mark
        # print(type(data._yaml_comment._items[0][0].start_mark))
        data.yaml_add_eol_comment('comment 2', key=3)
        exp = """
        - a   # comment 1
        - b
        - c
        - d   # comment 2
        - e
        - f     # comment 3
        """
        compare(data, exp)

    def test_seq_set_comment_on_existing_explicit_column_with_hash(self) -> None:
        data = load(
            """
        - a   # comment 1
        - b
        - c
        """
        )
        data.yaml_add_eol_comment('#  comment 2', key=1, column=6)
        exp = """
        - a   # comment 1
        - b   #  comment 2
        - c
        """
        compare(data, exp)

    # dict

    def test_dict_set_comment_on_existing_explicit_column(self) -> None:
        data = load(
            """
        a: 1   # comment 1
        b: 2
        c: 3
        d: 4
        e: 5
        """
        )
        data.yaml_add_eol_comment('comment 2', key='c', column=7)
        exp = """
        a: 1   # comment 1
        b: 2
        c: 3   # comment 2
        d: 4
        e: 5
        """
        compare(data, exp)

    def test_dict_overwrite_comment_on_existing_explicit_column(self) -> None:
        data = load(
            """
        a: 1   # comment 1
        b: 2
        c: 3
        d: 4
        e: 5
        """
        )
        data.yaml_add_eol_comment('comment 2', key='a', column=7)
        exp = """
        a: 1   # comment 2
        b: 2
        c: 3
        d: 4
        e: 5
        """
        compare(data, exp)

    def test_map_set_comment_on_existing_column_prev(self) -> None:
        data = load(
            """
            a: 1   # comment 1
            b: 2
            c: 3
            d: 4
            e: 5     # comment 3
            """
        )
        data.yaml_add_eol_comment('comment 2', key='b')
        exp = """
            a: 1   # comment 1
            b: 2   # comment 2
            c: 3
            d: 4
            e: 5     # comment 3
            """
        compare(data, exp)

    def test_map_set_comment_on_existing_column_next(self) -> None:
        data = load(
            """
            a: 1   # comment 1
            b: 2
            c: 3
            d: 4
            e: 5     # comment 3
            """
        )
        data.yaml_add_eol_comment('comment 2', key='d')
        exp = """
            a: 1   # comment 1
            b: 2
            c: 3
            d: 4     # comment 2
            e: 5     # comment 3
            """
        compare(data, exp)

    def test_map_set_comment_on_existing_column_further_away(self) -> None:
        """
        no comment line before or after, take the latest before
        the new position
        """
        data = load(
            """
            a: 1   # comment 1
            b: 2
            c: 3
            d: 4
            e: 5     # comment 3
            """
        )
        data.yaml_add_eol_comment('comment 2', key='c')
        print(round_trip_dump(data))
        exp = """
            a: 1   # comment 1
            b: 2
            c: 3   # comment 2
            d: 4
            e: 5     # comment 3
            """
        compare(data, exp)

    def test_before_top_map_rt(self) -> None:
        data = load(
            """
        a: 1
        b: 2
        """
        )
        data.yaml_set_start_comment('Hello\nWorld\n')
        exp = """
        # Hello
        # World
        a: 1
        b: 2
        """
        compare(data, exp.format(comment='#'))

    def test_before_top_map_replace(self) -> None:
        data = load(
            """
        # abc
        # def
        a: 1 # 1
        b: 2
        """
        )
        data.yaml_set_start_comment('Hello\nWorld\n')
        exp = """
        # Hello
        # World
        a: 1 # 1
        b: 2
        """
        compare(data, exp.format(comment='#'))

    def test_before_top_map_from_scratch(self) -> None:
        from ruyaml.comments import CommentedMap

        data = CommentedMap()
        data['a'] = 1
        data['b'] = 2
        data.yaml_set_start_comment('Hello\nWorld\n')
        # print(data.ca)
        # print(data.ca._items)
        exp = """
            # Hello
            # World
            a: 1
            b: 2
            """
        compare(data, exp.format(comment='#'))

    def test_before_top_seq_rt(self) -> None:
        data = load(
            """
        - a
        - b
        """
        )
        data.yaml_set_start_comment('Hello\nWorld\n')
        print(round_trip_dump(data))
        exp = """
        # Hello
        # World
        - a
        - b
        """
        compare(data, exp)

    def test_before_top_seq_rt_replace(self) -> None:
        s = """
        # this
        # that
        - a
        - b
        """
        data = load(s.format(comment='#'))
        data.yaml_set_start_comment('Hello\nWorld\n')
        print(round_trip_dump(data))
        exp = """
        # Hello
        # World
        - a
        - b
        """
        compare(data, exp.format(comment='#'))

    def test_before_top_seq_from_scratch(self) -> None:
        from ruyaml.comments import CommentedSeq

        data = CommentedSeq()
        data.append('a')
        data.append('b')
        data.yaml_set_start_comment('Hello\nWorld\n')
        print(round_trip_dump(data))
        exp = """
        # Hello
        # World
        - a
        - b
        """
        compare(data, exp.format(comment='#'))

    # nested variants
    def test_before_nested_map_rt(self) -> None:
        data = load(
            """
        a: 1
        b:
          c: 2
          d: 3
        """
        )
        data['b'].yaml_set_start_comment('Hello\nWorld\n')
        exp = """
        a: 1
        b:
        # Hello
        # World
          c: 2
          d: 3
        """
        compare(data, exp.format(comment='#'))

    def test_before_nested_map_rt_indent(self) -> None:
        data = load(
            """
        a: 1
        b:
          c: 2
          d: 3
        """
        )
        data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
        exp = """
        a: 1
        b:
          # Hello
          # World
          c: 2
          d: 3
        """
        compare(data, exp.format(comment='#'))
        print(data['b'].ca)

    def test_before_nested_map_from_scratch(self) -> None:
        from ruyaml.comments import CommentedMap

        data = CommentedMap()
        datab = CommentedMap()
        data['a'] = 1
        data['b'] = datab
        datab['c'] = 2
        datab['d'] = 3
        data['b'].yaml_set_start_comment('Hello\nWorld\n')
        exp = """
        a: 1
        b:
        # Hello
        # World
          c: 2
          d: 3
        """
        compare(data, exp.format(comment='#'))

    def test_before_nested_seq_from_scratch(self) -> None:
        from ruyaml.comments import CommentedMap, CommentedSeq

        data = CommentedMap()
        datab = CommentedSeq()
        data['a'] = 1
        data['b'] = datab
        datab.append('c')
        datab.append('d')
        data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
        exp = """
        a: 1
        b:
          # Hello
          # World
        - c
        - d
        """
        compare(data, exp.format(comment='#'))

    def test_before_nested_seq_from_scratch_block_seq_indent(self) -> None:
        from ruyaml.comments import CommentedMap, CommentedSeq

        data = CommentedMap()
        datab = CommentedSeq()
        data['a'] = 1
        data['b'] = datab
        datab.append('c')
        datab.append('d')
        data['b'].yaml_set_start_comment('Hello\nWorld\n', indent=2)
        exp = """
        a: 1
        b:
          # Hello
          # World
          - c
          - d
        """
        compare(data, exp.format(comment='#'), indent=4, block_seq_indent=2)

    def test_map_set_comment_before_and_after_non_first_key_00(self) -> None:
        # http://stackoverflow.com/a/40705671/1307905
        data = load(
            """
        xyz:
          a: 1    # comment 1
          b: 2

        test1:
          test2:
            test3: 3
                """
        )
        data.yaml_set_comment_before_after_key(
            'test1',
            'before test1 (top level)',
            after='before test2',
        )
        data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
        exp = """
        xyz:
          a: 1    # comment 1
          b: 2

        # before test1 (top level)
        test1:
          # before test2
          test2:
            # after test2
            test3: 3
        """
        compare(data, exp)

    def Xtest_map_set_comment_before_and_after_non_first_key_01(self) -> None:
        data = load(
            """
        xyz:
          a: 1    # comment 1
          b: 2

        test1:
          test2:
            test3: 3
        """
        )
        data.yaml_set_comment_before_after_key(
            'test1',
            'before test1 (top level)',
            after='before test2\n\n',
        )
        data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
        # EOL is needed here as dedenting gets rid of spaces (as well as does Emacs
        exp = """
        xyz:
          a: 1    # comment 1
          b: 2

        # before test1 (top level)
        test1:
          # before test2
          EOL
          test2:
            # after test2
            test3: 3
        """
        compare_eol(data, exp)

    # EOL is no longer necessary
    # fixed together with issue # 216
    def test_map_set_comment_before_and_after_non_first_key_01(self) -> None:
        data = load(
            """
        xyz:
          a: 1    # comment 1
          b: 2

        test1:
          test2:
            test3: 3
        """
        )
        data.yaml_set_comment_before_after_key(
            'test1',
            'before test1 (top level)',
            after='before test2\n\n',
        )
        data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
        exp = """
        xyz:
          a: 1    # comment 1
          b: 2

        # before test1 (top level)
        test1:
          # before test2

          test2:
            # after test2
            test3: 3
        """
        compare(data, exp)

    def Xtest_map_set_comment_before_and_after_non_first_key_02(self) -> None:
        data = load(
            """
        xyz:
          a: 1    # comment 1
          b: 2

        test1:
          test2:
            test3: 3
        """
        )
        data.yaml_set_comment_before_after_key(
            'test1',
            'xyz\n\nbefore test1 (top level)',
            after='\nbefore test2',
            after_indent=4,
        )
        data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
        # EOL is needed here as dedenting gets rid of spaces (as well as does Emacs
        exp = """
        xyz:
          a: 1    # comment 1
          b: 2

        # xyz

        # before test1 (top level)
        test1:
            EOL
            # before test2
          test2:
            # after test2
            test3: 3
        """
        compare_eol(data, exp)

    def test_map_set_comment_before_and_after_non_first_key_02(self) -> None:
        data = load(
            """
        xyz:
          a: 1    # comment 1
          b: 2

        test1:
          test2:
            test3: 3
        """
        )
        data.yaml_set_comment_before_after_key(
            'test1',
            'xyz\n\nbefore test1 (top level)',
            after='\nbefore test2',
            after_indent=4,
        )
        data['test1']['test2'].yaml_set_start_comment('after test2', indent=4)
        exp = """
        xyz:
          a: 1    # comment 1
          b: 2

        # xyz

        # before test1 (top level)
        test1:

            # before test2
          test2:
            # after test2
            test3: 3
        """
        compare(data, exp)

    # issue 32
    def test_yaml_add_eol_comment_issue_32(self):
        data = load(
            """
        items:
        - one: 1
          uno: '1'
        -  # item 2
          two: 2
          duo: '2'
        - three: 3
        """
        )

        data['items'].yaml_add_eol_comment('second pass', key=1)

        exp = """
        items:
        - one: 1
          uno: '1'
        -  # second pass
          two: 2
          duo: '2'
        - three: 3
        """

        compare(data, exp)

    def test_yaml_add_eol_comment_issue_32_ok(self):
        data = load(
            """
        items:
        - one
        - two  # item 2
        - three
        """
        )

        data['items'].yaml_add_eol_comment('second pass', key=1)

        exp = """
        items:
        - one
        - two  # second pass
        - three
        """

        compare(data, exp)

    # issue 33
    @pytest.mark.xfail(reason="open issue", raises=AssertionError)
    def test_yaml_set_start_comment_issue_33(self):
        data = load(
            """
        items:
        # item 1
        - one: 1
          uno: '1'
        # item 2
        - two: 2
          duo: '2'
        # item 3
        - three: 3
        """
        )

        data['items'][0].yaml_set_start_comment('uno')
        data['items'][1].yaml_set_start_comment('duo')
        data['items'][2].yaml_set_start_comment('tre')

        exp = """
        items:
        # uno
        - one: 1
          uno: '1'
        # duo
        - two: 2
          duo: '2'
        # tre
        - three: 3
        """

        compare(data, exp)
