# Copyright © Michal Čihař <michal@weblate.org>
#
# SPDX-License-Identifier: GPL-3.0-or-later

"""Testing of template tags."""

import datetime

from django.test import SimpleTestCase, TestCase
from django.utils import timezone
from django.utils.html import format_html

from weblate.accounts.models import Profile
from weblate.checks.tests.test_checks import MockLanguage, MockUnit
from weblate.lang.models import Language
from weblate.trans.models import Component, Project, Translation, Unit
from weblate.trans.templatetags.translations import (
    format_translation,
    get_location_links,
    naturaltime,
)
from weblate.trans.tests.test_views import FixtureTestCase

TEST_DATA = (
    (0, "now"),
    (1, "a second from now"),
    (-1, "a second ago"),
    (2, "2 seconds from now"),
    (-2, "2 seconds ago"),
    (60, "a minute from now"),
    (-60, "a minute ago"),
    (120, "2 minutes from now"),
    (-120, "2 minutes ago"),
    (3600, "an hour from now"),
    (-3600, "an hour ago"),
    (3600 * 2, "2 hours from now"),
    (-3600 * 2, "2 hours ago"),
    (3600 * 24, "tomorrow"),
    (-3600 * 24, "yesterday"),
    (3600 * 24 * 2, "2 days from now"),
    (-3600 * 24 * 2, "2 days ago"),
    (3600 * 24 * 7, "a week from now"),
    (-3600 * 24 * 7, "a week ago"),
    (3600 * 24 * 14, "2 weeks from now"),
    (-3600 * 24 * 14, "2 weeks ago"),
    (3600 * 24 * 30, "a month from now"),
    (-3600 * 24 * 30, "a month ago"),
    (3600 * 24 * 60, "2 months from now"),
    (-3600 * 24 * 60, "2 months ago"),
    (3600 * 24 * 365, "a year from now"),
    (-3600 * 24 * 365, "a year ago"),
    (3600 * 24 * 365 * 2, "2 years from now"),
    (-3600 * 24 * 365 * 2, "2 years ago"),
)


class NaturalTimeTest(SimpleTestCase):
    """Testing of natural time conversion."""

    def test_natural(self):
        now = timezone.now()
        for diff, expected in TEST_DATA:
            testdate = now + datetime.timedelta(seconds=diff)
            result = naturaltime(testdate, now)
            expected = format_html(
                '<span title="{}">{}</span>',
                testdate.replace(microsecond=0).isoformat(),
                expected,
            )
            self.assertEqual(
                expected,
                result,
                f"naturaltime({testdate}) {result!r} != {expected!r}",
            )


class LocationLinksTest(TestCase):
    def setUp(self):
        self.unit = Unit(
            translation=Translation(
                component=Component(
                    project=Project(slug="p", name="p"),
                    source_language=Language(),
                    slug="c",
                    name="c",
                    pk=-1,
                ),
                language=Language(),
            ),
            pk=-1,
        )
        self.unit.source_unit = self.unit
        self.profile = Profile()

    def test_empty(self):
        self.assertEqual(get_location_links(self.profile, self.unit), "")

    def test_numeric(self):
        self.unit.location = "123"
        self.assertEqual(get_location_links(self.profile, self.unit), "string ID 123")

    def test_filename(self):
        self.unit.location = "f&oo.bar:123"
        self.assertEqual(
            get_location_links(self.profile, self.unit), "f&amp;oo.bar:123"
        )

    def test_filenames(self):
        self.unit.location = "foo.bar:123,bar.foo:321"
        self.assertEqual(
            get_location_links(self.profile, self.unit),
            'foo.bar:123\n<span class="divisor">•</span>\nbar.foo:321',
        )

    def test_repowebs(self):
        self.unit.translation.component.repoweb = (
            "http://example.net/{{filename}}#L{{line}}"
        )
        self.unit.location = "foo.bar:123,bar.foo:321"
        self.assertHTMLEqual(
            get_location_links(self.profile, self.unit),
            """
            <a class="wrap-text"
                href="http://example.net/foo.bar#L123" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            foo.bar:123
            </a>
            <span class="divisor">•</span>
            <a class="wrap-text"
                href="http://example.net/bar.foo#L321" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            bar.foo:321
            </a>
            """,
        )

    def test_repoweb(self):
        self.unit.translation.component.repoweb = (
            "http://example.net/{{filename}}#L{{line}}"
        )
        self.unit.location = "foo.bar:123"
        self.assertHTMLEqual(
            get_location_links(self.profile, self.unit),
            """
            <a class="wrap-text"
                href="http://example.net/foo.bar#L123" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            foo.bar:123
            </a>
            """,
        )

    def test_user_url(self):
        self.unit.translation.component.repoweb = (
            "http://example.net/{{filename}}#L{{line}}"
        )
        self.profile.editor_link = "editor://open/?file={{filename}}&line={{line}}"
        self.unit.location = "foo.bar:123"
        self.assertHTMLEqual(
            get_location_links(self.profile, self.unit),
            """
            <a class="wrap-text"
                href="editor://open/?file=foo.bar&amp;line=123" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            foo.bar:123
            </a>
            """,
        )

    def test_filename_quote(self):
        self.unit.translation.component.repoweb = (
            "http://example.net/{{filename}}#L{{line}}"
        )
        self.unit.location = "foo+bar:321"
        self.assertHTMLEqual(
            get_location_links(self.profile, self.unit),
            """
            <a class="wrap-text"
                href="http://example.net/foo%2Bbar#L321" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            foo+bar:321
            </a>
            """,
        )

    def test_absolute_url(self):
        self.unit.translation.component.repoweb = (
            "http://example.net/{{filename}}#L{{line}}"
        )
        self.unit.location = (
            "foo.bar:123,bar.foo:321,https://example.com/foo,http://example.org/bar"
        )
        self.assertHTMLEqual(
            get_location_links(self.profile, self.unit),
            """
            <a class="wrap-text"
                href="http://example.net/foo.bar#L123" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            foo.bar:123
            </a>
            <span class="divisor">•</span>
            <a class="wrap-text"
                href="http://example.net/bar.foo#L321" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            bar.foo:321
            </a>
            <span class="divisor">•</span>
            <a class="wrap-text"
                href="https://example.com/foo" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            https://example.com/foo
            </a>
            <span class="divisor">•</span>
            <a class="wrap-text"
                href="http://example.org/bar" target="_blank"
                dir="ltr" rel="noopener noreferrer">
            http://example.org/bar
            </a>
            """,
        )


class TranslationFormatTestCase(FixtureTestCase):
    def setUp(self):
        super().setUp()
        self.translation = self.get_translation()

    def test_basic(self):
        self.assertEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            "Hello world",
        )

    def test_diff(self):
        self.assertEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                diff="Hello, world!",
            )["items"][0]["content"],
            "Hello<del>,</del> world<del>!</del>",
        )
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                diff="Hello  world",
            )["items"][0]["content"],
            """
            Hello
            <del>
                <span class="hlspace">
                    <span class="space-space"> </span>
                </span>
            </del>
            world
            """,
        )

    def test_diff_github_9821(self):
        unit = Unit(translation=self.translation)
        unit.all_flags = {"python-brace-format"}
        self.assertHTMLEqual(
            format_translation(
                ["由 {username} 邀请至 {project} 项目。"],
                self.component.source_language,
                diff="由 {username} 邀请至 {site_title}。",
                unit=unit,
            )["items"][0]["content"],
            """
            由
            <span class="hlcheck" data-value="{username}"><span class="highlight-number"></span>{username}</span>
             邀请至
             {<del>site_title}</del><ins>project} 项目</ins>。
            """,
        )

    def test_diff_whitespace(self):
        self.assertHTMLEqual(
            format_translation(
                ["Helloworld"],
                self.component.source_language,
                diff="Hello world",
            )["items"][0]["content"],
            """Hello
            <del>
                <span class="hlspace">
                    <span class="space-space"> </span>
                </span>
            </del>
            world
            """,
        )
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                diff="Helloworld",
            )["items"][0]["content"],
            """Hello
            <ins>
                <span class="hlspace">
                    <span class="space-space"> </span>
                </span>
            </ins>
            world
            """,
        )

    def test_diff_whitespace_changed(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello  world"],
                self.component.source_language,
                diff="Hello world",
            )["items"][0]["content"],
            """Hello
            <span class="hlspace">
                <span class="space-space">
                </span>
            </span>
            <ins>
                <span class="hlspace">
                    <span class="space-space">
                    </span>
                </span>
            </ins>
            world
            """,
        )

    def test_diff_newline(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                diff="Hello\nworld",
            )["items"][0]["content"],
            """Hello
            <del>
                <span class="hlspace">
                    <span class="space-nl"></span>
                </span><br />
            </del>
            <ins>
                <span class="hlspace">
                    <span class="space-space"> </span>
                </span>
            </ins>
            world
            """,
        )

    def test_diff_changed_whitespace(self):
        self.assertHTMLEqual(
            format_translation(
                ["     ${APP_NAME} is great"],
                self.component.source_language,
                diff="    App is great",
            )["items"][0]["content"],
            """
            <span class="hlspace">
                <span class="space-space"> </span>
                <span class="space-space"> </span>
                <span class="space-space"> </span>
                <span class="space-space"> </span>
            </span>
            <del>App</del>
            <ins>
                <span class="hlspace">
                    <span class="space-space"> </span>
                </span>
                ${APP_NAME}
            </ins>
            is great
            """,
        )

    def test_glossary(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                glossary=[
                    Unit(source="hello", target="ahoj", translation=self.translation)
                ],
            )["items"][0]["content"],
            """
            <span class="glossary-term"
                title="Glossary translation: ahoj">Hello</span>
            world
            """,
        )

    def test_glossary_overlap(self):
        self.maxDiff = None
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                glossary=[
                    Unit(
                        source="hello world",
                        target="ahoj svete",
                        translation=self.translation,
                    ),
                    Unit(source="hello", target="ahoj", translation=self.translation),
                ],
            )["items"][0]["content"],
            """
            <span class="glossary-term" title="Glossary translation: ahoj svete, ahoj">
                Hello
            </span>
            <span class="glossary-term" title="Glossary translation: ahoj svete">
                world
            </span>
            """,
        )

    def test_glossary_brackets(self):
        self.assertHTMLEqual(
            format_translation(
                ["[Hello] world"],
                self.component.source_language,
                glossary=[
                    Unit(source="[hello]", target="ahoj", translation=self.translation)
                ],
            )["items"][0]["content"],
            """
            <span class="glossary-term"
                title="Glossary translation: ahoj">[Hello]</span>
            world
            """,
        )

    def test_glossary_space(self):
        self.assertHTMLEqual(
            format_translation(
                ["text  Hello world"],
                self.component.source_language,
                glossary=[
                    Unit(source="hello", target="ahoj", translation=self.translation)
                ],
            )["items"][0]["content"],
            """
            text
            <span class="hlspace">
                <span class="space-space">
                </span>
                <span class="space-space">
                </span>
            </span>
            <span class="glossary-term"
                title="Glossary translation: ahoj">Hello</span>
            world
            """,
        )

    def test_glossary_escape(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                glossary=[
                    Unit(
                        source="hello", target='<b>ahoj"', translation=self.translation
                    )
                ],
            )["items"][0]["content"],
            """
            <span class="glossary-term"
                title="Glossary translation: &lt;b&gt;ahoj&quot;">Hello</span>
            world
            """,
        )

    def test_glossary_multi(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello glossary"],
                self.component.source_language,
                glossary=[
                    Unit(source="hello", target="ahoj", translation=self.translation),
                    Unit(
                        source="glossary", target="glosář", translation=self.translation
                    ),
                ],
            )["items"][0]["content"],
            """
            <span class="glossary-term"
                title="Glossary translation: ahoj">Hello</span>
            <span class="glossary-term"
                title="Glossary translation: glosář">glossary</span>
            """,
        )

    def test_glossary_format(self):
        unit = Unit(translation=self.translation)
        unit.all_flags = {"php-format"}
        self.assertHTMLEqual(
            format_translation(
                ["%3$sHow"],
                self.component.source_language,
                glossary=[
                    Unit(
                        source="show", target="zobrazit", translation=self.translation
                    ),
                ],
                unit=unit,
            )["items"][0]["content"],
            """
            <span class="hlcheck" data-value="%3$s">
            <span class="highlight-number"></span>
            %3$s
            </span>
            How
            """,
        )

    def test_highlight(self):
        unit = self.translation.unit_set.get(id_hash=2097404709965985808)
        self.assertHTMLEqual(
            format_translation(
                unit.get_source_plurals(),
                unit.translation.language,
                unit=unit,
            )["items"][0]["content"],
            """
            Orangutan has
            <span class="hlcheck" data-value="%d">
                <span class="highlight-number"></span>%d
            </span>
            banana.<span class="hlspace"><span class="space-nl">
            </span>
            </span>
            <br/>
            """,
        )

    def test_search(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello world"],
                self.component.source_language,
                search_match="world",
            )["items"][0]["content"],
            """Hello <span class="hlmatch">world</span>""",
        )

    def test_whitespace(self):
        self.assertHTMLEqual(
            format_translation(
                [" Hello world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            <span class="hlspace">
                <span class="space-space">
                </span>
            </span>
            Hello
            world
            """,
        )
        self.assertHTMLEqual(
            format_translation(
                ["  Hello world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            <span class="hlspace">
                <span class="space-space">
                </span>
                <span class="space-space">
                </span>
            </span>
            Hello
            world
            """,
        )
        self.assertHTMLEqual(
            format_translation(
                ["Hello   world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            Hello
            <span class="hlspace">
                <span class="space-space">
                </span>
                <span class="space-space">
                </span>
                <span class="space-space">
                </span>
            </span>
            world
            """,
        )
        self.assertHTMLEqual(
            format_translation(
                ["Hello world "],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            Hello
            world
            <span class="hlspace"><span class="space-space">
            </span>
            </span>
            """,
        )

    def test_whitespace_special(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello\u00A0world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            Hello
            <span class="hlspace">
                <span class="space-space" title="NO-BREAK SPACE">
                    \u00A0
                </span>
            </span>
            world
            """,
        )

    def test_whitespace_newline(self):
        self.assertHTMLEqual(
            format_translation(
                ["Hello\n world"],
                self.component.source_language,
            )[
                "items"
            ][0]["content"],
            """
            Hello
            <span class="hlspace">
                <span class="space-nl">
                </span>
            </span><br>
            <span class="hlspace">
                <span class="space-space">
                </span>
            </span>
            world
            """,
        )


class DiffTestCase(SimpleTestCase):
    """Testing of HTML diff function."""

    def html_diff(self, diff, source):
        unit = MockUnit(source=source)
        return format_translation(
            unit.get_source_plurals(),
            unit.translation.component.source_language,
            diff=diff,
        )["items"][0]["content"]

    def test_same(self):
        self.assertEqual(self.html_diff("first text", "first text"), "first text")

    def test_add(self):
        self.assertEqual(
            self.html_diff("first text", "first new text"), "first <ins>new </ins>text"
        )

    def test_unicode(self):
        self.assertEqual(
            self.html_diff("zkouška text", "zkouška nový text"),
            "zkouška <ins>nový </ins>text",
        )

    def test_remove(self):
        self.assertHTMLEqual(
            self.html_diff("first old text", "first text"),
            """
            first
            <del>old
             <span class="hlspace">
             <span class="space-space">
             </span>
            </del>
            text""",
        )

    def test_replace(self):
        self.assertEqual(
            self.html_diff("first old text", "first new text"),
            "first <del>old</del><ins>new</ins> text",
        )

    def test_format_diff(self):
        unit = MockUnit(source="Hello word!")
        self.assertEqual(
            format_translation(
                unit.get_source_plurals(),
                unit.translation.component.source_language,
                diff="Hello world!",
            )["items"][0]["content"],
            "Hello wor<del>l</del>d!",
        )

    def test_format_diff_whitespace(self):
        unit = MockUnit(source="Hello world!")
        self.assertHTMLEqual(
            format_translation(
                unit.get_source_plurals(),
                unit.translation.component.source_language,
                diff="Hello world! ",
            )["items"][0]["content"],
            'Hello world!<del><span class="hlspace"><span class="space-space">'
            " </span></span></del>",
        )

    def test_format_diff_add_space(self):
        unit = MockUnit(source="Hello.  World.")
        self.assertHTMLEqual(
            format_translation(
                unit.get_source_plurals(),
                unit.translation.component.source_language,
                diff="Hello. World.",
            )["items"][0]["content"],
            """
            Hello.
            <ins>
                <span class="hlspace">
                    <span class="space-space"></span>
                </span>
            </ins>
            <span class="hlspace">
                <span class="space-space"></span>
            </span>
            World.
            """,
        )

    def test_format_entities(self):
        unit = MockUnit(source="'word'")
        self.assertEqual(
            format_translation(
                unit.get_source_plurals(),
                unit.translation.component.source_language,
                diff='"word"',
            )["items"][0]["content"],
            "<del>&quot;</del><ins>&#x27;</ins>word<del>&quot;</del><ins>&#x27;</ins>",
        )

    def test_fmtsearchmatch(self):
        self.assertEqual(
            format_translation(
                ["Hello world!"], MockLanguage("en"), search_match="hello"
            )["items"][0]["content"],
            '<span class="hlmatch">Hello</span> world!',
        )
