from io import BytesIO

from translate.convert import po2ts
from translate.storage import po

from . import test_convert


class TestPO2TS:
    @staticmethod
    def po2ts(posource):
        """Helper that converts po source to ts source without requiring files."""
        inputfile = BytesIO(posource.encode())
        inputpo = po.pofile(inputfile)
        convertor = po2ts.po2ts()
        output = BytesIO()
        convertor.convertstore(inputpo, output)
        return output.getvalue().decode("utf-8")

    @staticmethod
    def singleelement(storage):
        """Checks that the pofile contains a single non-header element, and returns it."""
        assert len(storage.units) == 1
        return storage.units[0]

    def test_simpleunit(self) -> None:
        """Checks that a simple po entry definition converts properly to a ts entry."""
        minipo = r'''#: term.cpp
msgid "Term"
msgstr "asdf"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        assert "<name>term.cpp</name>" in tsfile
        assert "<source>Term</source>" in tsfile
        assert "<translation>asdf</translation>" in tsfile
        assert "<comment>" not in tsfile

    def test_simple_unicode_unit(self) -> None:
        """Checks that a simple unit with unicode strings."""
        minipo = r'''#: unicode.cpp
msgid "ßource"
msgstr "†arget"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        print(type(tsfile))
        assert "<name>unicode.cpp</name>" in tsfile
        assert "<source>ßource</source>" in tsfile
        assert "<translation>†arget</translation>" in tsfile

    def test_fullunit(self) -> None:
        """Check that an entry with various settings is converted correctly."""
        posource = """# Translator comment
#. Automatic comment
#: location.cpp:100
msgid "Source"
msgstr "Target"
"""
        tsfile = self.po2ts(posource)
        print(tsfile)
        # The other section are a duplicate of test_simplentry
        # FIXME need to think about auto vs trans comments maybe in TS v1.1
        assert "<comment>Translator comment</comment>" in tsfile

    def test_fuzzyunit(self) -> None:
        """Check that we handle fuzzy units correctly."""
        posource = '''#: term.cpp
#, fuzzy
msgid "Source"
msgstr "Target"'''
        tsfile = self.po2ts(posource)
        print(tsfile)
        assert """<translation type="unfinished">Target</translation>""" in tsfile

    def test_obsolete(self) -> None:
        """Test that we can take back obsolete messages."""
        posource = '''#. (obsolete)
#: term.cpp
msgid "Source"
msgstr "Target"'''
        tsfile = self.po2ts(posource)
        print(tsfile)
        assert """<translation type="obsolete">Target</translation>""" in tsfile

    def test_duplicates(self) -> None:
        """Test that we can handle duplicates in the same context block."""
        posource = """#: @@@#1
msgid "English"
msgstr "a"

#: @@@#3
msgid "English"
msgstr "b"
"""
        tsfile = self.po2ts(posource)
        print(tsfile)
        assert tsfile.find("English") != tsfile.rfind("English")

    def test_linebreak(self) -> None:
        """Test that we can handle linebreaks."""
        minipo = r'''#: linebreak.cpp
msgid "Line 1\n"
"Line 2"
msgstr "Linea 1\n"
"Linea 2"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        print(type(tsfile))
        assert "<name>linebreak.cpp</name>" in tsfile
        assert (
            r"""<source>Line 1
Line 2</source>"""
            in tsfile
        )
        assert (
            r"""<translation>Linea 1
Linea 2</translation>"""
            in tsfile
        )

    def test_linebreak_consecutive(self) -> None:
        """Test that we can handle consecutive linebreaks."""
        minipo = r'''#: linebreak.cpp
msgid "Line 1\n"
"\n"
"Line 3"
msgstr "Linea 1\n"
"\n"
"Linea 3"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        print(type(tsfile))
        assert "<name>linebreak.cpp</name>" in tsfile
        assert (
            r"""<source>Line 1

Line 3</source>"""
            in tsfile
        )
        assert (
            r"""<translation>Linea 1

Linea 3</translation>"""
            in tsfile
        )

    def test_plural(self) -> None:
        """Test that plural forms are correctly converted from PO to TS."""
        minipo = r'''#: ExamplePO
msgid "%d day ago"
msgid_plural "%d days ago"
msgstr[0] "%d dźeń tamu"
msgstr[1] "%d dni tamu"
msgstr[2] "%d dźon tamu"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        assert "<name>ExamplePO</name>" in tsfile
        # The source should merge singular and plural with (s) notation
        assert "<source>%d day(s) ago</source>" in tsfile
        assert 'numerus="yes"' in tsfile
        assert "<numerusform>%d dźeń tamu</numerusform>" in tsfile
        assert "<numerusform>%d dni tamu</numerusform>" in tsfile
        assert "<numerusform>%d dźon tamu</numerusform>" in tsfile

    def test_plural_fuzzy(self) -> None:
        """Test that fuzzy plural forms are handled correctly."""
        minipo = r'''#: FuzzyContext
#, fuzzy
msgid "%d item"
msgid_plural "%d items"
msgstr[0] "%d punkt"
msgstr[1] "%d punkty"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        assert "<name>FuzzyContext</name>" in tsfile
        assert "<source>%d item(s)</source>" in tsfile
        assert 'numerus="yes"' in tsfile
        assert 'type="unfinished"' in tsfile
        assert "<numerusform>%d punkt</numerusform>" in tsfile
        assert "<numerusform>%d punkty</numerusform>" in tsfile

    def test_unit_without_location(self) -> None:
        """Test that units without location comments are still converted."""
        # This is a regression test for the issue where strings without
        # location comments (no #: line) were not being converted to TS
        minipo = r'''msgid "File Path"
msgstr "Ruta del archivo"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        # The unit should be present even without a location
        assert "<source>File Path</source>" in tsfile
        assert "<translation>Ruta del archivo</translation>" in tsfile
        # It should be in a context with an empty name
        assert "<name></name>" in tsfile

    def test_mixed_units_with_and_without_location(self) -> None:
        """Test conversion of both units with and without location comments."""
        minipo = r'''#: mainWindow#1
msgid "Machine"
msgstr "Máquina"

msgid "File Path"
msgstr "Ruta del archivo"'''
        tsfile = self.po2ts(minipo)
        print(tsfile)
        # Both units should be present
        assert "<source>Machine</source>" in tsfile
        assert "<translation>Máquina</translation>" in tsfile
        assert "<source>File Path</source>" in tsfile
        assert "<translation>Ruta del archivo</translation>" in tsfile
        # First should have mainWindow context
        assert "<name>mainWindow</name>" in tsfile
        # Second should have empty context
        assert "<name></name>" in tsfile


class TestPO2TSCommand(test_convert.TestConvertCommand, TestPO2TS):
    """Tests running actual po2ts commands on files."""

    convertmodule = po2ts

    expected_options = [
        "-c CONTEXT, --context=CONTEXT",
        "-t TEMPLATE, --template=TEMPLATE",
    ]
