# SPDX-FileCopyrightText: © 2023 Christian BUHTZ <c.buhtz@posteo.jp>
# SPDX-FileCopyrightText: © 2025 Michael Olie <aolie1794@gmail.com>
#
# 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 folder LICENSES or go to <https://www.gnu.org/licenses/#GPL>.
"""Tests about generating HTML from Runs using `exporter._html_from_runs()`."""
# pylint: disable=protected-access,missing-function-docstring
import unittest
from hyperorg import exporter
from hyperorg import textruns
from hyperorg.content import Node


def _reset_node_class():
    Node.nodes = {}
    Node.heading_ids = {}
    Node.input_dir = 'input_dir'
    exporter.Exporter.output_dir = 'output_dir'


class HtmlFromRuns_Markup(unittest.TestCase):  # pylint: disable=C0103
    """Test HTML out of markup runs"""

    def test_no_markup_text(self):
        runs = ['foobar']

        self.assertEqual(exporter._html_from_runs(runs), 'foobar')

    def test_markup(self):
        runs = [
            textruns.Markup('foobar', textruns.Markup.Type.IMPORTANT)
        ]

        self.assertEqual(
            exporter._html_from_runs(runs),
            '<strong>foobar</strong>')


class HtmlFromRuns_Links(unittest.TestCase):  # pylint: disable=C0103
    """Test HTML out of link runs"""

    # pylint: disable=protected-access
    # pylint: disable=missing-function-docstring
    # pylint: disable=too-many-public-methods

    @classmethod
    def setUpClass(cls):
        # pylint: disable=duplicate-code
        def _mock_me(*args, **kwargs):  # pylint: disable=unused-argument
            pass
        cls._establish_file_link = exporter._establish_file_link
        exporter._establish_file_link = _mock_me

    @classmethod
    def tearDownClass(cls):
        # pylint: disable=duplicate-code
        exporter._establish_file_link = cls._establish_file_link

    def setUp(self):
        _reset_node_class()

    def test_orgid(self):
        Node.nodes['123'] = Node([], '123_foo.org')
        runs = [
            textruns.Link.from_org_string('id:123][normal'),
            ' and ',
            textruns.Link.from_org_string('id:123][two words'),
            ' and ',
            textruns.Link.from_org_string('id:123][with\nlinebreak'),
        ]

        expect_html = '<a href="123_foo.html">normal</a> and ' \
            '<a href="123_foo.html">two words</a> and ' \
            '<a href="123_foo.html">with\nlinebreak</a>'

        self.assertEqual(exporter._html_from_runs(runs), expect_html)

    def test_orgid_without_label(self):
        Node.nodes['123'] = Node([], '123_foo.org')
        runs = [textruns.Link.from_org_string('id:123')]

        expect_html = '<a href="123_foo.html">123</a>'

        self.assertEqual(exporter._html_from_runs(runs), expect_html)

    def test_orgid_target_unknown(self):
        """Org-roam-ID unknown with label"""
        runs = [textruns.Link.from_org_string('id:123][normal')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", OrgID, label ...
            r'.*title=.*Unknown.*OrgID.*id\:123.*normal.*'
            # ... style class, label
            r'class=\"invalid-link\".*normal.*'
        )

    def test_orgid_target_unknown_without_label(self):
        """Org-roam-ID unknown without a label"""
        runs = [textruns.Link.from_org_string('id:123')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", OrgID, label ...
            r'.*title=.*Unknown*.OrgID.*id\:123.*'
            # ... style class, label
            r'class=\"invalid-link\".*123.*'
        )

    def test_roam_unknown(self):
        """Roam link (is always unknown)"""
        # textruns.Link('roam:target'),
        runs = [textruns.Link.from_org_string('roam:word][label')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", ROAM, label ...
            r'.*title=.*Unknown.*ROAM.*roam\:word.*'
            # ... style class, label
            r'class=\"invalid-link\".*label.*'
        )

    def test_roam_unknown_without_label(self):
        """Roam link (is always unknown) without label"""
        # textruns.Link('roam:target'),
        runs = [textruns.Link.from_org_string('roam:word')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", ROAM ...
            r'.*title=.*Unknown.*ROAM.*roam\:word.*'
            # ... style class, target
            r'class=\"invalid-link\".*word.*'
        )

    def test_https(self):
        """Hyperlinks with and without labels to href."""
        test_data = [
            # Link with label
            (
                [textruns.Link.from_org_string('https://foo.bar][foobar')],
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">foobar</a>'
            ),
            # Link without label
            (
                [textruns.Link.from_org_string('https://foo.bar')],
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">https://foo.bar</a>'
            ),
            # two links with(out) label
            (
                [
                    textruns.Link.from_org_string('https://foo.bar][foobar'),
                    ' and ',
                    textruns.Link.from_org_string('https://foo.bar')
                ],
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">foobar</a> and '
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">https://foo.bar</a>'
            ),
            # two links other way around
            (
                [
                    textruns.Link.from_org_string('https://foo.bar'),
                    ' and ',
                    textruns.Link.from_org_string('https://foo.bar][foobar')
                ],
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">https://foo.bar</a> and '
                '<a target="_blank" rel="noopener noreferrer" '
                'href="https://foo.bar">foobar</a>'
            ),
        ]

        for runs, expect in test_data:
            self.assertEqual(exporter._html_from_runs(runs), expect)

    def test_https_linebreaks(self):
        """Hyperlinks with linebreaks."""
        runs = [textruns.Link.from_org_string('https://foo.bar][foo\nbar')]
        expect = '<a target="_blank" rel="noopener noreferrer" ' \
                 'href="https://foo.bar">foo\nbar</a>'

        self.assertEqual(exporter._html_from_runs(runs), expect)

    def test_doi(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('doi:10.1055/a-1961-1145')]
            ),
            'DOI: <a target="_blank" rel="noopener noreferrer" '
            'href="https://doi.org/10.1055/a-1961-1145">'
            '10.1055/a-1961-1145</a>'
        )

    def test_doi_label(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string(
                    'doi:10.1055/a-1961-1145][Inanspruchnahme')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="https://doi.org/10.1055/a-1961-1145">'
            'Inanspruchnahme</a>'
        )

    def test_mailto(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('mailto:c.buhtz@posteo.jp')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="mailto:c.buhtz@posteo.jp">c.buhtz@posteo.jp</a>'
        )

    def test_mailto_label(self):
        self.assertEqual(
            exporter._html_from_runs([
                textruns.Link.from_org_string(
                    'mailto:c.buhtz@posteo.jp][Maintainer'
                )
            ]),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="mailto:c.buhtz@posteo.jp">Maintainer</a>'
        )

    def test_file(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('file:foobar.dat')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="foobar.dat">foobar.dat</a>'
        )

    def test_file_label(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('file:foobar.dat][FOOBAR.DAT')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="foobar.dat">FOOBAR.DAT</a>'
        )

    def test_no_type(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('foobar.dat')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="foobar.dat">foobar.dat</a>'
        )

    def test_no_type_label(self):
        self.assertEqual(
            exporter._html_from_runs(
                [textruns.Link.from_org_string('foobar.dat][LaBeL')]
            ),
            '<a target="_blank" rel="noopener noreferrer" '
            'href="foobar.dat">LaBeL</a>'
        )

    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
        """
        runs = [textruns.Link.from_org_string('http://foo.bar][foobar')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", ROAM, label ...
            r'.*title=.*security.*HTTP.*alternative.*http\://foo.bar.*'
            # ... style class, label
            r'class=\"insecure-link\".*foobar.*'
        )

    def test_unsecure_http_without_label(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
        """
        runs = [textruns.Link.from_org_string('http://foo.bar')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            # title attribute, "Unknown", ROAM, label ...
            r'.*title=.*security.*HTTP.*alternative.*http\://foo.bar.*'
            # ... style class, label
            r'class=\"insecure-link\".*foo.bar.*'
        )

    def test_unknown_type(self):
        """Link with unknown type (protocol)."""
        runs = [textruns.Link.from_org_string('fancytype:rainbow][label')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            r'.*title=.*link type.*fancytype.*unknown.*rainbow.*label.*'
            r'class=\"invalid-link\".*fancytype.*label.*'
        )

    def test_unknown_type_without_label(self):
        """Link with unknown type (protocol) without a label."""
        runs = [textruns.Link.from_org_string('fancytype:rainbow')]

        self.assertRegex(
            exporter._html_from_runs(runs),
            r'.*title=.*link type.*fancytype.*unknown.*rainbow.*'
            r'class=\"invalid-link\".*fancytype.*rainbow.*'
        )


class HtmlFromRuns_InlineImages(unittest.TestCase):  # pylint: disable=C0103
    """Test HTML out of inline images"""
    # pylint: disable=protected-access
    @classmethod
    def setUpClass(cls):
        def _mock_me(*args, **kwargs):  # pylint: disable=unused-argument
            pass
        cls._establish_file_link = exporter._establish_file_link
        exporter._establish_file_link = _mock_me

    @classmethod
    def tearDownClass(cls):
        exporter._establish_file_link = cls._establish_file_link

    def test_inline_image(self):
        """Inline image"""
        sut = [
            'foo',
            textruns.Image('target.png'),
            'bar'
        ]

        self.assertEqual(
            exporter._html_from_runs(sut),
            'foo<img src="target.png" alt="target.png" />bar'
        )

    def test_inline_image_as_link(self):
        """Linked inline image"""
        sut = [
            'foo',
            textruns.Link.from_org_string('target.png][file:image.png'),
            'bar'
        ]

        self.assertEqual(
            exporter._html_from_runs(sut),
            'foo'
            '<a target="_blank" rel="noopener noreferrer" href="target.png">'
            '<img src="image.png" alt="image.png" />'
            '</a>bar'
        )


class HtmlFromRuns_Math(unittest.TestCase):  # pylint: disable=C0103
    """Test HTML out of Math runs"""

    def test_inline(self):
        sut = [textruns.InlineMath('e = mc^{2}')]

        expected_html = r'<span class="math">\(e = mc^{2}\)</span>'

        self.assertEqual(exporter._html_from_runs(sut), expected_html)
