import functools
from typing import Callable, Tuple, List, Union, ItemsView

import pytest

from helpers.md_and_html_convertor import MdAndHTMLConvertorLowLevel, MdAndHTMLConvertor


def _test_helper(function: Callable[[str], str], text_in: str, expected_output: str):
    function_output = function(text_in)
    assert function_output == expected_output, f"Test: {text_in} |\nOutput: \t\t{function_output} |\nExpected: \t{expected_output}"


def swap_input_with_result(data: Union[List[Tuple[str, str]], ItemsView[str, str]]) -> List[Tuple[str, str]]:
    return [(x[1], x[0]) for x in data]


# The following tests are according to spec, but won't work (that same way they don't work in scan2report).
# "***test***": "***test***",
# "****": "****",
# "**test": "**test"
# "**test**test": "**test**test"

# '\ue022' = MdAndHTMLConvertor._HTML_TO_RESERVED_CHARS["&"]

FUNC_SCAN2REPORT_CUSTOM_ESCAPED_TO_PWNDOC = MdAndHTMLConvertor.custom_md_to_pwndoc_html

FUNC_PWNDOC_TO_SCAN2REPORT_CUSTOM_ESCAPED = MdAndHTMLConvertor.html_to_custom_pwndoc_html
# FUNC_PWNDOC_TO_SCAN2REPORT_NATIVE = MdAndHTMLConvertorSimplerInterface.pwndoc_template_to_scan2report_template_native

FUNC_CUSTOM_ESCAPE_ONLY = MdAndHTMLConvertorLowLevel.custom_escape_html
FUNC_CUSTOM_UNESCAPE_ONLY = MdAndHTMLConvertorLowLevel.reverse_custom_escaping_of_html
# FUNC_MD_TO_HTML = MdAndHTMLConvertor.md_to_html


class TestLowLevelFunctions:
    @staticmethod
    def test_custom_unescaping():
        _test_helper(FUNC_CUSTOM_UNESCAPE_ONLY, '\ue022', '&')

    @staticmethod
    def test_custom_escaping():
        _test_helper(FUNC_CUSTOM_ESCAPE_ONLY, '&', '\ue022')

    @staticmethod
    @pytest.mark.parametrize("test_input, expected", [
        ("**test**", "<strong>test</strong>"),
        ("__test__", "<em>test</em>"),
        ("*test*", "*test*"),
    ])
    def test_md_to_html(test_input: str, expected: str):
        _test_helper(MdAndHTMLConvertorLowLevel.md_to_html, test_input, expected)

    @staticmethod
    @pytest.mark.parametrize("test_input", [
        "<p></p>",
        "<p>A</p><p>B</p>",
        "<p>A</p><img/><p>B</p>",
        "<p>A<br></p><img/><p>B</p>",
    ])
    def test_valid_html_is_not_modified(test_input: str):
        val = test_input

        for _ in range(10):
            val = MdAndHTMLConvertorLowLevel.html_to_pwndoc_html(val)

        assert val == test_input

    # todo: test html to md?


class TestHighLevelConversions:
    @staticmethod
    @pytest.mark.parametrize("test_input, expected", [
        ("", "<p></p>"),
        ("**test**", "<p><strong>test</strong></p>"),
        ("<bold>test</bold>", "<p>&lt;bold&gt;test&lt;/bold&gt;</p>"),  # scan2report shouldn't contain raw HTML unless force_import_of_html_for_basic_tags is enabled
        ("test\n\nEND", "<p>test<br><br>END</p>"),
        ("&", "<p>&amp;</p>"),

    ])
    def test_scan2report_to_pwndoc(test_input: str, expected: str):
        _test_helper(FUNC_SCAN2REPORT_CUSTOM_ESCAPED_TO_PWNDOC, test_input, expected)

    @staticmethod
    @pytest.mark.parametrize("test_input", [
        "<p></p>",
        "<p>&amp;</p>",
        '<p>before <img alt="example.jpg" src="6352e52b5137a10013dd06f2"/> after</p>'  # the raw is <img src="6352e52b5137a10013dd06f2" alt="example.jpg">, but bs4 make small modifications

    ])
    def test_html_to_md_and_back(test_input: str):
        html = test_input

        md_text = FUNC_PWNDOC_TO_SCAN2REPORT_CUSTOM_ESCAPED(html)
        html_back = FUNC_SCAN2REPORT_CUSTOM_ESCAPED_TO_PWNDOC(md_text)
        assert html_back == html, f"\nTest (html): \t\t\t'{html}' |\nHTML -> MD: \t\t\t'{md_text}' |\nHTML -> MD -> HTML : \t'{html_back}'"

    # todo: test export to scan2report native
