# SPDX-FileCopyrightText: © 2023 Christian BUHTZ <c.buhtz@posteo.jp>
#
# SPDX-License-Identifier: GPL-3.0-only
#
# This file is part of the program "Hyperorg" which is released under
# GNU General Public License v3 (GPLv3). See LICENSES directory
# or go to <https://spdx.org/licenses/GPL-3.0-only.html>.
"""Tests about runs"""
import unittest
from hyperorg import textruns


class CutIntoRuns(unittest.TestCase):
    """Test conversion from text to runs.

    Here with `textruns.cut_into_runs()` the combination of the three
    functions `textruns._token_positions()`, `textruns._token_pairs()` and
    `textruns._runs_from_token_pairs()` is tested.
    """

    # pylint: disable=missing-function-docstring
    def test_text_only(self):
        """One simple text run."""
        text = 'Another one got caught today'
        result = textruns.cut_into_runs(text)

        self.assertEqual(len(result), 1)
        self.assertIsInstance(result[0], str)

    def test_empty_string(self):
        """Empty strings.

        This is e.g. useful with list elements where empty
        list entries are allowed.
        """
        result = textruns.cut_into_runs('')
        self.assertEqual(len(result), 1)
        self.assertEqual(result[0], '')

    def test_verbatim_only(self):
        sut = textruns.cut_into_runs('=--foo=')

        self.assertEqual(len(sut), 1)
        self.assertEqual(
            sut[0],
            textruns.Markup('--foo', textruns.Markup.Type.VERBATIM)
        )

    def test_markup_combined(self):
        """Bigger text with multiple markup in it."""
        text = (
            '+Governments+ of the *Industrial World*, you weary =giants= of '
            'flesh and steel, I come from _Cyberspace_, the new home of Mind. '
            'On /behalf of the future/, I ask you of the past to leave us '
            'alone. You are ~not welcome~ among us. You have no sovereignty '
            'where we gather.'
        )

        result = textruns.cut_into_runs(text)

        self.assertEqual(len(result), 12)

        expected_parts = {
            1: ' of the ',
            3: ', you weary ',
            5: ' of flesh and steel, I come from ',
            7: ', the new home of Mind. On ',
            9: ', I ask you of the past to leave us alone. You are ',
            11: ' among us. You have no sovereignty where we gather.'
        }
        for idx, exp in expected_parts.items():
            self.assertEqual(str(result[idx]), exp)

        expected_parts = {
            0: 'Governments',
            2: 'Industrial World',
            4: 'giants',
            6: 'Cyberspace',
            8: 'behalf of the future',
            10: 'not welcome',
        }
        for idx, exp in expected_parts.items():
            self.assertEqual(str(result[idx].runs[0]), exp)

        marker_at_index = [
            (0, textruns.Markup.Type.STRIKETHROUGH),
            (2, textruns.Markup.Type.IMPORTANT),
            (4, textruns.Markup.Type.VERBATIM),
            (6, textruns.Markup.Type.UNDERLINED),
            (8, textruns.Markup.Type.EMPHASIZED),
            (10, textruns.Markup.Type.CODE)
        ]

        for idx, markup_type in marker_at_index:
            self.assertEqual(result[idx].markup_type, markup_type)

    def test_links_combined_two(self):
        test = (
            'This is [[id:1234][World]] and [[roam:Buhtz][Me]] ende'
        )

        result = textruns.cut_into_runs(test)

        self.assertEqual(len(result), 5)

        self.assertEqual(result[0], 'This is ')
        self.assertEqual(result[1][0], 'World')
        self.assertEqual(result[1].target, '1234')
        self.assertEqual(result[2], ' and ')
        self.assertEqual(result[3][0], 'Me')
        self.assertEqual(result[3].target, 'Buhtz')
        self.assertEqual(result[4], ' ende')

    def test_links_combined_all(self):
        test = (
            'This is [[https://world.me][World]] and [[roam:Buhtz][Me]] '
            'or just [[roam:Buhtz]]. Also try [[id:12345][with label]] or '
            'that [[https://out.there]] somewhere.'
        )

        result = textruns.cut_into_runs(test)

        self.assertEqual(len(result), 11)

        self.assertEqual(result[1][0], 'World')
        self.assertEqual(result[1].target, 'world.me')
        self.assertEqual(result[3][0], 'Me')
        self.assertEqual(result[3].target, 'Buhtz')
        self.assertEqual(len(result[5]), 0)
        self.assertEqual(result[5].target, 'Buhtz')
        self.assertEqual(result[7][0], 'with label')
        self.assertEqual(result[7].target, '12345')
        self.assertEqual(len(result[9]), 0)
        self.assertEqual(result[9].target, 'out.there')

    def test_links_and_markup_neighbors(self):
        test = 'you _weary_ [[https://big.com][giants]]'

        result = textruns.cut_into_runs(test)

        self.assertEqual(len(result), 4)

        self.assertEqual(result[0], 'you ')
        self.assertEqual(result[1][0], 'weary')
        self.assertEqual(result[1].markup_type,
                         textruns.Markup.Type.UNDERLINED)
        self.assertEqual(result[2], ' ')
        self.assertEqual(result[3][0], 'giants')
        self.assertEqual(result[3].target, 'big.com')
        self.assertEqual(result[3].link_type, textruns.Link.Type.HTTPS)

    def test_links_variants(self):
        test_data = [
            (  # orgid
                'This [[id:0123456789][is an OrgId link]] test.',
                {
                    1: [textruns.Link.Type.ORGID,
                        '0123456789', 'is an OrgId link'],
                },
            ),
            (  # roam with labels
                'This [[roam:0123456789][is a Roam link]] test.',
                {
                    1: [textruns.Link.Type.ROAM,
                        '0123456789', 'is a Roam link'],
                },
            ),
            (  # roam without labels
                'This [[roam:Buhtz]] test.',
                {
                    1: [textruns.Link.Type.ROAM, 'Buhtz', None],
                },
            ),
            (  # https with label
                'This [[https://my.world][Buhtz]] test.',
                {
                    1: [textruns.Link.Type.HTTPS, 'my.world', 'Buhtz'],
                },
            ),
            (  # two orgid
                'This [[id:01234][Foo]] and [[id:5678][bar]].',
                {
                    1: [textruns.Link.Type.ORGID, '01234', 'Foo'],
                    3: [textruns.Link.Type.ORGID, '5678', 'bar'],
                },
            ),
        ]

        for text, links in test_data:
            result = textruns.cut_into_runs(text)

            for idx, lnk in links.items():
                kind, target, label = lnk

                self.assertEqual(result[idx].link_type, kind)
                self.assertEqual(result[idx].target, target)

                if label:
                    self.assertEqual(result[idx][0], label)
                else:
                    self.assertEqual(len(result[idx]), 0)

    def test_links_label_newline(self):
        text = 'This [[id:123][is an\nOrgId link]] test.'
        result = textruns.cut_into_runs(text)

        self.assertEqual(result[1][0], 'is an\nOrgId link')

    def test_url_with_slash_at_end(self):
        """If there is a slash at the end of an URL it was (see #64)
        interpreted as emphasized inline markup.
        """
        text = 'Foo [[https://my.world/]] bar'
        result = textruns.cut_into_runs(text)

        self.assertEqual(result[0], 'Foo ')
        self.assertEqual(len(result[1]), 0)
        self.assertEqual(result[1].target, 'my.world/')
        self.assertEqual(result[2], ' bar')

    def test_multiple_links_and_markup_all(self):
        test = (
            '*Governments* of the [[roam:Industrial World][IW]], you _weary_'
            ' [[https://big.com][giants]] /of/ [[id:1854][flesh]] and '
            '=steel=, +I+ come from [[roam:Cyberspace]], the ~new home~ of '
            '[[https://Mind.org]]. On behalf of the future'
        )

        result = textruns.cut_into_runs(test)

        self.assertEqual(len(result), 22)

        # the key here is the index in ``result``
        check_links = {
            2: ('Industrial World', 'IW', textruns.Link.Type.ROAM),
            6: ('big.com', 'giants', textruns.Link.Type.HTTPS),
            10: ('1854', 'flesh', textruns.Link.Type.ORGID),
            16: ('Cyberspace', None, textruns.Link.Type.ROAM),
            20: ('Mind.org', None, textruns.Link.Type.HTTPS)
        }

        for idx_key, links in check_links.items():
            target, label, kind = links
            self.assertEqual(result[idx_key].target, target)

            if label:
                self.assertEqual(result[idx_key][0], label)
            else:
                self.assertEqual(len(result[idx_key]), 0)

            self.assertEqual(result[idx_key].link_type, kind)

        check_markup = {
            0: ('Governments', textruns.Markup.Type.IMPORTANT),
            4: ('weary', textruns.Markup.Type.UNDERLINED),
            8: ('of', textruns.Markup.Type.EMPHASIZED),
            12: ('steel', textruns.Markup.Type.VERBATIM),
            14: ('I', textruns.Markup.Type.STRIKETHROUGH),
            18: ('new home', textruns.Markup.Type.CODE)
        }

        for idx_key, markup in check_markup.items():
            text, kind = markup
            self.assertEqual(len(result[idx_key]), 1)
            self.assertEqual(result[idx_key][0], text)
            self.assertEqual(result[idx_key].markup_type, kind)

    def test_markup_without_prefix_suffix(self):
        sut = textruns.cut_into_runs('+Government+')

        self.assertEqual(len(sut), 1)

        self.assertEqual(sut[0][0], 'Government')
        self.assertEqual(
            sut[0].markup_type,
            textruns.Markup.Type.STRIKETHROUGH
        )

    def test_markup_prefix_suffix(self):
        result = textruns.cut_into_runs('foo +Government+ bar')

        self.assertEqual(len(result), 3)
        self.assertEqual(str(result[0]), 'foo ')
        self.assertEqual(str(result[2]), ' bar')

        self.assertIsInstance(result[1], textruns.Markup)
        markup = result[1]

        self.assertEqual(str(markup.runs[0]), 'Government')
        self.assertEqual(
            markup.markup_type,
            textruns.Markup.Type.STRIKETHROUGH
        )

    def test_markup_variant_blanks(self):
        result = textruns.cut_into_runs(' +Government+ ')

        self.assertEqual(len(result), 3)
        self.assertEqual(str(result[0]), ' ')
        self.assertEqual(str(result[2]), ' ')

        self.assertEqual(str(result[1].runs[0]), 'Government')
        self.assertEqual(result[1].markup_type,
                         textruns.Markup.Type.STRIKETHROUGH)

    def test_markup_two(self):
        for markup_type in textruns.Markup.Type:
            text = 'Governments {m}of{m} the {m}Industrial{m} World' \
                   .format(m=markup_type.value)

            result = textruns.cut_into_runs(text)

            self.assertEqual(len(result), 5, markup_type)
            self.assertEqual(str(result[0]), 'Governments ', markup_type)
            self.assertEqual(str(result[1].runs[0]), 'of', markup_type)
            self.assertEqual(str(result[2]), ' the ', markup_type)
            self.assertEqual(str(result[3].runs[0]), 'Industrial', markup_type)
            self.assertEqual(str(result[4]), ' World', markup_type)

    def test_markup_two_without_pre_suffix(self):
        for markup_type in textruns.Markup.Type:
            text = '{m}Governments{m} of {m}the{m} Industrial {m}World{m}' \
                   .format(m=markup_type.value)

            result = textruns.cut_into_runs(text)

            self.assertEqual(len(result), 5, markup_type)
            self.assertEqual(str(result[0].runs[0]),
                             'Governments',
                             markup_type)
            self.assertEqual(str(result[1]), ' of ', markup_type)
            self.assertEqual(str(result[2].runs[0]), 'the', markup_type)
            self.assertEqual(str(result[3]), ' Industrial ', markup_type)
            self.assertEqual(str(result[4].runs[0]), 'World', markup_type)

    def test_markup_entangled(self):
        """First wins."""
        text = '/italics ~code/ verbatim~'
        result = textruns.cut_into_runs(text)

        self.assertEqual(len(result), 2)
        self.assertEqual(str(result[0].runs[0]), 'italics ~code')
        self.assertEqual(str(result[1]), ' verbatim~')

    def test_unsecure_http(self):
        """HTTP links are not secure but will be parsed.

            See #29 about an improvement of that behavior
            unsafe (because of HTTP-only) hyperlinks not parsed
        """
        text = 'This [[http://foo.bar][foobar]] is not safe.'
        result = textruns.cut_into_runs(text)

        self.assertEqual(len(result), 3)
        self.assertEqual(result[1][0], 'foobar')
        self.assertEqual(result[1].target, 'foo.bar')
        self.assertEqual(result[1].link_type, textruns.Link.Type.HTTP)
