from __future__ import annotations

import os

from translate.convert import md2po
from translate.storage.po import pofile

from . import test_convert


class TestMD2PO(test_convert.TestConvertCommand):
    convertmodule = md2po
    defaultoptions = {"progress": "none"}

    expected_options = [
        "-P, --pot",
        "-t TEMPLATE, --template=TEMPLATE",
        "--duplicates=DUPLICATESTYLE",
        "--multifile=MULTIFILESTYLE",
        "-m MAXLENGTH, --maxlinelength=MAXLENGTH",
        "--no-code-blocks",
        "--no-frontmatter",
        "--no-placeholders",
    ]

    def test_markdown_file_with_multifile_single(self) -> None:
        self.given_markdown_file()
        self.run_command("file.md", "test.po", multifile="single")
        self.then_po_file_is_written()

    def test_markdown_file_with_multifile_onefile(self) -> None:
        self.given_markdown_file()
        self.run_command("file.md", "test.po", multifile="onefile")
        self.then_po_file_is_written()

    def test_markdown_directory_with_multifile_single(self) -> None:
        self.given_directory_of_markdown_files()
        self.run_command("mddir", "podir", multifile="single")
        assert os.path.isdir(self.get_testfilename("podir"))
        assert os.path.isfile(self.get_testfilename("podir/file1.po"))
        assert os.path.isfile(self.get_testfilename("podir/file2.po"))
        content = self.read_testfile("podir/file1.po").decode()
        assert "Content of file 1" in content
        assert "Content of file 2" not in content

    def test_markdown_directory_with_multifile_onefile(self) -> None:
        self.given_directory_of_markdown_files()
        self.run_command("mddir", "test.po", multifile="onefile")
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert "Content of file 1" in content
        assert "Content of file 2" in content

    def test_globbed_markdown_files_write_pots_to_output_root(self) -> None:
        input_dir = os.path.join("resources", "locales", "en")
        output_dir = os.path.join("resources", "locales")
        one_md = os.path.join(input_dir, "one.md")
        two_md = os.path.join(input_dir, "two.md")

        self.create_testfile(one_md, "# One\nHello one")
        self.create_testfile(two_md, "# Two\nHello two")

        self.run_command(
            one_md,
            two_md,
            output_dir,
            pot=True,
            timestamp=True,
        )

        assert os.path.isfile(
            self.get_testfilename(os.path.join(output_dir, "one.pot"))
        )
        assert os.path.isfile(
            self.get_testfilename(os.path.join(output_dir, "two.pot"))
        )
        assert not os.path.exists(
            self.get_testfilename(os.path.join(input_dir, "one.pot"))
        )
        assert not os.path.exists(
            self.get_testfilename(os.path.join(input_dir, "two.pot"))
        )

    def given_markdown_file(self, content: str | None = None) -> None:
        if content is None:
            content = "# Markdown\nYou are only coming through in waves."
        self.create_testfile("file.md", content)

    @staticmethod
    def quoted_payloads(block: str) -> list[str]:
        return [
            line[1:-1]
            for line in block.splitlines()
            if line.startswith('"') and line.endswith('"')
        ]

    def test_po_max_line_length_wraps_output(self) -> None:
        long_text = (
            "This sentence contains enough words to require wrapping in PO output."
        )
        self.given_markdown_file(long_text)
        self.run_command("file.md", "test.po", maxlinelength=24)
        content = self.read_testfile("test.po").decode()
        unit_block = content.split("#: file.md:1\n", 1)[1].split("msgstr", 1)[0]

        assert f'msgid "{long_text}"' not in unit_block
        assert unit_block.startswith('msgid ""\n')
        assert all(len(line) <= 24 for line in self.quoted_payloads(unit_block))

    def test_po_max_line_length_zero_disables_wrapping(self) -> None:
        long_text = (
            "This sentence contains enough words to require wrapping in PO output."
        )
        self.given_markdown_file(long_text)
        self.run_command("file.md", "test.po", maxlinelength=0)
        content = self.read_testfile("test.po").decode()

        assert f'msgid "{long_text}"' in content

    def test_markdown_frontmatter(self) -> None:
        self.given_markdown_file(
            content="""---
date: 2024-02-02T04:14:54-08:00
draft: false
params:
  author: John Smith
title: Example
weight: 10
---

# Markdown
You are only coming through in waves.
"""
        )
        self.run_command("file.md", "test.po")
        self.then_po_file_is_written()
        output = pofile()
        with open(self.get_testfilename("test.po"), "rb") as handle:
            output.parse(handle)
        assert len(output.units) == 4
        assert (
            output.units[1].source
            == """---
date: 2024-02-02T04:14:54-08:00
draft: false
params:
  author: John Smith
title: Example
weight: 10
---

"""
        )
        assert output.units[1].getlocations() == ["file.md:1"]

    def test_markdown_frontmatter_not_extracted_when_disabled(self) -> None:
        self.given_markdown_file(
            content="""---
title: Example
author: John Smith
---

# Markdown
You are only coming through in waves.
"""
        )
        os.chdir(self.testdir)
        try:
            self.convertmodule.main(
                ["file.md", "test.po", "--progress=none", "--no-frontmatter"]
            )
        finally:
            os.chdir(self.rundir)
        self.then_po_file_is_written()
        output = pofile()
        with open(self.get_testfilename("test.po"), "rb") as handle:
            output.parse(handle)
        sources = [unit.source for unit in output.units]
        assert "---\ntitle: Example\nauthor: John Smith\n---\n\n" not in sources

    def test_markdown_code_blocks_not_extracted_when_disabled(self) -> None:
        self.given_markdown_file(
            content="""# Markdown

You are only coming through in waves.

```python
def hello():
    return "world"
```
"""
        )
        os.chdir(self.testdir)
        try:
            self.convertmodule.main(
                ["file.md", "test.po", "--progress=none", "--no-code-blocks"]
            )
        finally:
            os.chdir(self.rundir)
        self.then_po_file_is_written()
        output = pofile()
        with open(self.get_testfilename("test.po"), "rb") as handle:
            output.parse(handle)
        sources = [unit.source for unit in output.units]
        assert not any("hello" in s for s in sources)

    def test_markdown_hard_line_break_extraction(self) -> None:
        self.given_markdown_file(
            content=(
                "- Use multiple keys: `OWC_ENCRYPTION_KEYS='Pj...48=,cx...Fw='`  \n"
                "  If you use multiple keys, only the first one encrypts the data.\n"
                "  The others are only used to decrypt the data.\n"
            )
        )
        self.run_command("file.md", "test.po")
        output = pofile()
        with open(self.get_testfilename("test.po"), "rb") as handle:
            output.parse(handle)

        sources = [unit.source for unit in output.units]
        assert (
            "Use multiple keys: `OWC_ENCRYPTION_KEYS='Pj...48=,cx...Fw='`\n"
            "If you use multiple keys, only the first one encrypts the data. "
            "The others are only used to decrypt the data."
        ) in sources

    def test_markdown_directory_ignores_txt_files(self) -> None:
        self.given_directory_of_markdown_files()
        self.create_testfile("mddir/notes.txt", "Text file content")
        self.run_command("mddir", "podir", multifile="single")
        assert os.path.isdir(self.get_testfilename("podir"))
        assert os.path.isfile(self.get_testfilename("podir/file1.po"))
        assert os.path.isfile(self.get_testfilename("podir/file2.po"))
        assert not os.path.isfile(self.get_testfilename("podir/notes.po"))

    def test_explicit_txt_file_is_processed(self) -> None:
        self.create_testfile("file.txt", "# Heading\nText file content")
        self.run_command("file.txt", "test.po")
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert "Text file content" in content

    def given_directory_of_markdown_files(self) -> None:
        os.makedirs("mddir", exist_ok=True)
        self.create_testfile("mddir/file1.md", "# Heading\nContent of file 1")
        self.create_testfile("mddir/file2.md", "# Heading\nContent of file 2")

    def then_po_file_is_written(self) -> str:
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert "coming through" in content
        return content

    def test_markdown_hyperlink_extraction(self) -> None:
        """Test that markdown hyperlinks are extracted with {n} placeholder markers by default."""
        self.given_markdown_file(
            "The [OSPO Alliance EN](https://ospo-alliance.org) website\n"
        )
        self.run_command("file.md", "test.po")
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert 'msgid "The [OSPO Alliance EN]{1} website"' in content

    def test_markdown_multiple_hyperlinks_extraction(self) -> None:
        """Test that multiple markdown hyperlinks are extracted with placeholder markers by default."""
        self.given_markdown_file(
            "Visit [Google](https://google.com) and [GitHub](https://github.com) for more.\n"
        )
        self.run_command("file.md", "test.po")
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert 'msgid "Visit [Google]{1} and [GitHub]{2} for more."' in content

    def test_markdown_hyperlink_extraction_no_placeholders(self) -> None:
        """Test that --no-placeholders preserves full link syntax in msgids."""
        self.given_markdown_file(
            "The [OSPO Alliance EN](https://ospo-alliance.org) website\n"
        )
        os.chdir(self.testdir)
        try:
            self.convertmodule.main(
                [
                    "file.md",
                    "test.po",
                    "--progress=none",
                    "--no-placeholders",
                ]
            )
        finally:
            os.chdir(self.rundir)
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert (
            'msgid "The [OSPO Alliance EN](https://ospo-alliance.org) website"'
            in content
        )

    def test_markdown_multiple_hyperlinks_extraction_no_placeholders(self) -> None:
        """Test that --no-placeholders preserves full links for multiple hyperlinks."""
        self.given_markdown_file(
            "Visit [Google](https://google.com) and [GitHub](https://github.com) for more.\n"
        )
        os.chdir(self.testdir)
        try:
            self.convertmodule.main(
                [
                    "file.md",
                    "test.po",
                    "--progress=none",
                    "--no-placeholders",
                ]
            )
        finally:
            os.chdir(self.rundir)
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        assert "[Google](https://google.com)" in content
        assert "[GitHub](https://github.com)" in content
        assert "{1}" not in content
        assert "{2}" not in content

    def test_markdown_translation_ignore_sections(self) -> None:
        """Test that content between translate:off and translate:on is not extracted."""
        self.given_markdown_file(
            content="""# Welcome

This text will be translated.

<!-- translate:off -->

```python
def example():
    return "This code won't be extracted"
```

[link-ref]: https://example.com "Link Title"

<!-- translate:on -->

This text will also be translated.
"""
        )
        self.run_command("file.md", "test.po")
        assert os.path.isfile(self.get_testfilename("test.po"))
        content = self.read_testfile("test.po").decode()
        # Verify that translatable content is extracted
        assert "Welcome" in content
        assert "This text will be translated." in content
        assert "This text will also be translated." in content
        # Verify that ignored content is NOT extracted
        assert "This code won't be extracted" not in content
        assert "link-ref" not in content
        assert "Link Title" not in content

    def test_markdown_with_template_basic(self):
        """Test md2po with template file for basic matching."""
        # Create template (source language)
        self.create_testfile(
            "template.md",
            "# Hello\n\nWorld\n",
        )
        # Create translated file
        self.create_testfile(
            "translated.md",
            "# Hola\n\nMundo\n",
        )
        # Run conversion with template
        self.run_command("translated.md", "output.po", template="template.md")

        # Verify output
        assert os.path.isfile(self.get_testfilename("output.po"))
        content = self.read_testfile("output.po").decode()
        assert "Hello" in content  # source
        assert "Hola" in content  # target
        assert "World" in content  # source
        assert "Mundo" in content  # target

    def test_translated_markdown_input_with_source_template(self) -> None:
        """Test converting existing translated Markdown back to PO."""
        self.create_testfile(
            "docs/index.md",
            "# Welcome\n\nRead the documentation before continuing.\n",
        )
        self.create_testfile(
            "docs/index.nl.md",
            "# Welkom\n\nLees de documentatie voordat u doorgaat.\n",
        )

        self.run_command(
            "docs/index.nl.md",
            "index.po",
            template="docs/index.md",
        )

        output = pofile()
        with open(self.get_testfilename("index.po"), "rb") as handle:
            output.parse(handle)
        units = {
            unit.source: unit.target for unit in output.units if not unit.isheader()
        }

        assert units["Welcome"] == "Welkom"
        assert (
            units["Read the documentation before continuing."]
            == "Lees de documentatie voordat u doorgaat."
        )
        assert "Welkom" not in units

    def test_markdown_with_template_complex(self):
        """Test md2po with template file for complex Markdown structure."""
        # Create template (source language)
        template_md = """# Welcome

First paragraph

Second paragraph

## Subsection

- Item 1
- Item 2
"""

        # Create translated file (same structure, different content)
        translated_md = """# Bienvenue

Premier paragraphe

Deuxième paragraphe

## Sous-section

- Article 1
- Article 2
"""

        self.create_testfile("template_complex.md", template_md)
        self.create_testfile("translated_complex.md", translated_md)

        # Run conversion
        self.run_command(
            "translated_complex.md",
            "output_complex.po",
            template="template_complex.md",
        )

        # Verify output
        assert os.path.isfile(self.get_testfilename("output_complex.po"))
        content = self.read_testfile("output_complex.po").decode()

        # Check source strings
        assert "Welcome" in content
        assert "First paragraph" in content
        assert "Second paragraph" in content
        assert "Subsection" in content
        assert "Item 1" in content
        assert "Item 2" in content

        # Check target strings
        assert "Bienvenue" in content
        assert "Premier paragraphe" in content
        assert "Deuxième paragraphe" in content
        assert "Sous-section" in content
        assert "Article 1" in content
        assert "Article 2" in content

    def test_markdown_frontmatter_with_template(self) -> None:
        self.create_testfile(
            "template.md",
            """---
title: Example
author: John Smith
---

# Welcome
Body text.
""",
        )
        self.create_testfile(
            "translated.md",
            """---
title: Ukazka
author: Jan Novak
---

# Vitejte
Prelozeny text.
""",
        )

        self.run_command("translated.md", "output.po", template="template.md")

        content = self.read_testfile("output.po").decode()
        assert "title: Example" in content
        assert "author: John Smith" in content
        assert "title: Ukazka" in content
        assert "author: Jan Novak" in content
        assert "Welcome" in content
        assert "Vitejte" in content

    def test_markdown_frontmatter_with_template_not_extracted_when_disabled(
        self,
    ) -> None:
        self.create_testfile(
            "template.md",
            """---
title: Example
author: John Smith
---

# Welcome
Body text.
""",
        )
        self.create_testfile(
            "translated.md",
            """---
title: Ukazka
author: Jan Novak
---

# Vitejte
Prelozeny text.
""",
        )

        os.chdir(self.testdir)
        try:
            self.convertmodule.main(
                [
                    "translated.md",
                    "output.po",
                    "--progress=none",
                    "--template=template.md",
                    "--no-frontmatter",
                ]
            )
        finally:
            os.chdir(self.rundir)

        content = self.read_testfile("output.po").decode()
        assert "title: Example" not in content
        assert "author: John Smith" not in content
        assert "title: Ukazka" not in content
        assert "author: Jan Novak" not in content
        assert "Welcome" in content
        assert "Vitejte" in content
