import pytest
from assertpy import assert_that

from httpmessageprocessorutil.sanitizer import Nh3Sanitizer


SANITIZED_CASES = [
    ('<script>alert("xss")</script>', ""),
    ("<img src=x onerror=alert(1)>", ""),
    ('"><button onwebkitmouseforcechanged=alert(sessionStorage.getItem("key"))>', '">'),
    ("<b>tag</b>", "tag"),
    ("<ScRiPt>alert(1)</ScRiPt>", ""),
    ("<svg onload=alert(1)>", ""),
    ("<svg><script>alert(1)</script></svg>", ""),
    ("<iframe src=javascript:alert(1)>", ""),
    ("<body onload=alert(1)>", ""),
    ("<video><source onerror=alert(1)>", ""),
    ("<math><mtext><script>alert(1)</script>", ""),
    ('<a href="javascript:alert(1)">click</a>', "click"),
    ('<a href="data:text/html,<script>alert(1)</script>">x</a>', "x"),
    ("<!--<script>alert(1)</script>-->", ""),
    ("<![CDATA[<script>alert(1)</script>]]>", "alert(1)]]>"),
    ("<script>alert(1)", ""),
    ("<scr<script>ipt>alert(1)</script>", "ipt>alert(1)"),
    ("<<SCRIPT>alert(1)//<</SCRIPT>", "<"),
    ("</>", ''),
    ("https://example.com/?q=<script>alert(1)</script>", "https://example.com/?q="),
    ("https://evil.com/<img src=x onerror=alert(1)>", "https://evil.com/"),
    ("Visit https://example.com/<script>alert(1)</script> now", "Visit https://example.com/ now"),
    ('<a href="https://example.com">link</a>', "link"),
]

NON_STRING_INPUTS = [42, 3.14, True, None]

PRESERVED_INPUTS = [
    "",
    "hello world",
    "5 &gt; 3 &amp; 2 &lt; 4",
    "&gt;",
    ">",
    "<",
    "&",
    "<&>",
    ">&<",
    "< & >",
    "> & <",
    "São Paulo",
    "name with -> arrow",
    "name with <- arrow",
    "name with [brackets]",
    "Names for companies&ltda",
    "Buy this product > gain this one",
    "This product has %discount",
    "&#60;script&#62;alert(1)&#60;/script&#62;",
    "&#x3C;script&#x3E;alert(1)&#x3C;/script&#x3E;",
    "https://example.com",
    "http://example.com/path",
    "https://example.com/search?q=hello&lang=en",
    "https://example.com/path?a=1&b=2&c=3",
    "https://user:pass@example.com:8080/path",
    "https://example.com/#fragment",
    "ftp://files.example.com/file.txt",
    "https://example.com/path?name=João",
    "Visit https://example.com for more",
    "mailto:user@example.com",
    "See https://example.com/docs?section=intro&lang=pt-br#top for details",
]


class TestNh3SanitizerSanitize:
    @pytest.mark.parametrize("value,expected", SANITIZED_CASES)
    def test_inputIsSanitized(self, value, expected):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize(value)

        assert_that(result).is_equal_to(expected)

    @pytest.mark.parametrize("value", NON_STRING_INPUTS)
    def test_nonStringValue_isReturnedAsIs(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize(value)

        assert_that(result).is_equal_to(value)

    @pytest.mark.parametrize("value", PRESERVED_INPUTS)
    def test_inputIsPreserved(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize(value)

        assert_that(result).is_equal_to(value)


class TestNh3SanitizerSanitizeRequest:
    @pytest.mark.parametrize("value,expected", SANITIZED_CASES)
    def test_inputIsSanitized(self, value, expected):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_request(value)

        assert_that(result).is_equal_to(expected)

    @pytest.mark.parametrize("value", NON_STRING_INPUTS)
    def test_nonStringValue_isReturnedAsIs(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_request(value)

        assert_that(result).is_equal_to(value)

    @pytest.mark.parametrize("value", PRESERVED_INPUTS)
    def test_inputIsPreserved(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_request(value)

        assert_that(result).is_equal_to(value)


class TestNh3SanitizerSanitizeResponse:
    @pytest.mark.parametrize("value,expected", SANITIZED_CASES)
    def test_inputIsSanitized(self, value, expected):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_response(value)

        assert_that(result).is_equal_to(expected)

    @pytest.mark.parametrize("value", NON_STRING_INPUTS)
    def test_nonStringValue_isReturnedAsIs(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_response(value)

        assert_that(result).is_equal_to(value)

    @pytest.mark.parametrize("value", PRESERVED_INPUTS)
    def test_inputIsPreserved(self, value):
        sanitizer = Nh3Sanitizer()

        result = sanitizer.sanitize_response(value)

        assert_that(result).is_equal_to(value)


class TestNh3SanitizerTypesToSanitize:
    def test_onlySanitizesStrings(self):
        sanitizer = Nh3Sanitizer()

        types = sanitizer._types_to_sanitize()

        assert_that(types).is_equal_to([str])
