from urllib.parse import quote

import pytest

from streamlink.utils.url import absolute_url, is_insecure_scheme, prepend_www, update_qsd, update_scheme, url_concat, url_equal


@pytest.mark.parametrize(
    ("baseurl", "url", "expected"),
    [
        ("http://test.se", "/test", "http://test.se/test"),
        ("http://test.se", "http/test.se/test", "http://test.se/http/test.se/test"),
        ("http://test.se", "http://test2.se/test", "http://test2.se/test"),
    ],
)
def test_absolute_url(baseurl, url, expected):
    assert expected == absolute_url(baseurl, url)


@pytest.mark.parametrize(
    ("url", "expected"),
    [
        ("http://test.se/test", "http://www.test.se/test"),
        ("http://www.test.se", "http://www.test.se"),
    ],
)
def test_prepend_www(url, expected):
    assert expected == prepend_www(url)


@pytest.mark.parametrize(
    ("current_scheme", "target_scheme", "expected"),
    [
        ("http", "", False),
        ("https", "", False),
        ("file", "", False),
        ("http", "http", False),
        ("http", "https", False),
        ("https", "https", False),
        ("https", "http", True),
        ("file", "file", False),
        ("http", "file", True),
        ("https", "file", True),
    ],
)
def test_is_insecure_scheme(current_scheme: str, target_scheme: str, expected: bool):
    assert is_insecure_scheme(current_scheme, target_scheme) == expected


@pytest.mark.parametrize(
    ("args", "expected"),
    [
        pytest.param(
            ("https://other.com/bar", "http://example.com/foo"),
            "https://example.com/foo",
            id="override-target-http-scheme",
        ),
        pytest.param(
            ("http://other.com/bar", "https://example.com/foo"),
            "http://example.com/foo",
            id="override-target-https-scheme",
        ),
        pytest.param(
            ("http://other.com/bar", "https://example.com/foo", False),
            "https://example.com/foo",
            id="keep-target-https-scheme-no-force",
        ),
        pytest.param(
            ("https://other.com/bar", "http://example.com/foo", False),
            "http://example.com/foo",
            id="keep-target-http-scheme-no-force",
        ),
        pytest.param(
            ("https://other.com/bar", "//example.com/foo"),
            "https://example.com/foo",
            id="add-current-scheme-to-schemeless-target",
        ),
        pytest.param(
            ("https://other.com/bar", "//example.com/foo", False),
            "https://example.com/foo",
            id="add-current-scheme-to-schemeless-target-no-force",
        ),
        pytest.param(
            ("https://other.com/bar", "example.com/foo"),
            "https://example.com/foo",
            id="add-current-scheme-to-target-string",
        ),
        pytest.param(
            ("https://other.com/bar", "example.com/foo", False),
            "https://example.com/foo",
            id="add-current-scheme-to-target-string-no-force",
        ),
        pytest.param(
            ("http://", "127.0.0.1:1234"),
            "http://127.0.0.1:1234",
            id="implicit-scheme-with-ipv4-port",
        ),
        pytest.param(
            ("http://", "foo.bar:1234"),
            "http://foo.bar:1234",
            id="implicit-scheme-with-hostname-port",
        ),
        pytest.param(
            ("foo.1+2-bar://baz", "FOO.1+2-BAR://qux"),
            "foo.1+2-bar://qux",
            id="weird-schemes",
        ),
        pytest.param(
            ("https://", "file://./foo", False),
            "file://./foo",
            id="keep-target",
        ),
        pytest.param(
            ("https://", "file:///foo", False),
            "file:///foo",
            id="keep-target-no-netloc",
        ),
    ],
)
def test_update_scheme(args, expected):
    assert update_scheme(*args) == expected


def test_url_equal():
    assert url_equal("http://test.com/test", "http://test.com/test")
    assert not url_equal("http://test.com/test", "http://test.com/test2")

    assert url_equal("http://test.com/test", "http://test.com/test2", ignore_path=True)
    assert url_equal("http://test.com/test", "https://test.com/test", ignore_scheme=True)
    assert not url_equal("http://test.com/test", "https://test.com/test")

    assert url_equal("http://test.com/test", "http://test.com/test#hello", ignore_fragment=True)
    assert url_equal("http://test.com/test", "http://test2.com/test", ignore_netloc=True)
    assert not url_equal("http://test.com/test", "http://test2.com/test1", ignore_netloc=True)


def test_url_concat():
    assert url_concat("http://test.se", "one", "two", "three") == "http://test.se/one/two/three"
    assert url_concat("http://test.se", "/one", "/two", "/three") == "http://test.se/one/two/three"
    assert url_concat("http://test.se/one", "../two", "three") == "http://test.se/two/three"
    assert url_concat("http://test.se/one", "../two", "../three") == "http://test.se/three"


def test_update_qsd():
    assert update_qsd("http://test.se?one=1&two=3", {"two": 2}) == "http://test.se?one=1&two=2"
    assert update_qsd("http://test.se?one=1&two=3", remove=["two"]) == "http://test.se?one=1"
    assert update_qsd("http://test.se?one=1&two=3", {"one": None}, remove="*") == "http://test.se?one=1"
    assert update_qsd("http://test.se", {"one": "", "two": ""}) == "http://test.se?one=&two=", "should add empty params"
    assert update_qsd("http://test.se?one=", {"one": None}) == "http://test.se?one=", "should leave empty params unchanged"
    assert update_qsd("http://test.se?one=", keep_blank_values=False) == "http://test.se", "should strip blank params"
    assert update_qsd("http://test.se?one=&two=", {"one": None}, keep_blank_values=False) == "http://test.se?one=", \
        "should leave one"  # fmt: skip
    assert update_qsd("http://test.se?&two=", {"one": ""}, keep_blank_values=False) == "http://test.se?one=", \
        "should set one blank"  # fmt: skip
    assert update_qsd("http://test.se?one=", {"two": 2}) == "http://test.se?one=&two=2"

    assert update_qsd("http://test.se?foo=%3F", {"bar": "!"}) == "http://test.se?foo=%3F&bar=%21", \
        "urlencode - encoded URL"  # fmt: skip
    assert update_qsd("http://test.se?foo=?", {"bar": "!"}) == "http://test.se?foo=%3F&bar=%21", \
        "urlencode - fix URL"  # fmt: skip
    assert update_qsd("http://test.se?foo=?", {"bar": "!"}, quote_via=lambda s, *_: s) == "http://test.se?foo=?&bar=!", \
        "urlencode - dummy quote method"  # fmt: skip
    assert update_qsd("http://test.se", {"foo": "/ "}) == "http://test.se?foo=%2F+", \
        "urlencode - default quote_plus"  # fmt: skip
    assert update_qsd("http://test.se", {"foo": "/ "}, safe="/", quote_via=quote) == "http://test.se?foo=/%20", \
        "urlencode - regular quote with reserved slash"  # fmt: skip
    assert update_qsd("http://test.se", {"foo": "/ "}, safe="", quote_via=quote) == "http://test.se?foo=%2F%20", \
        "urlencode - regular quote without reserved slash"  # fmt: skip
