import pytest

from yarl import URL

# with_*


def test_with_scheme():
    url = URL("http://example.com")
    assert str(url.with_scheme("https")) == "https://example.com"


def test_with_scheme_uppercased():
    url = URL("http://example.com")
    assert str(url.with_scheme("HTTPS")) == "https://example.com"


@pytest.mark.parametrize(
    ("scheme"),
    [
        ("http"),
        ("https"),
        ("HTTP"),
    ],
)
def test_with_scheme_for_relative_url(scheme: str) -> None:
    """Test scheme can be set for relative URL."""
    lower_scheme = scheme.lower()
    msg = (
        "scheme replacement is not allowed for "
        f"relative URLs for the {lower_scheme} scheme"
    )
    with pytest.raises(ValueError, match=msg):
        assert URL("path/to").with_scheme(scheme)


def test_with_scheme_for_relative_file_url() -> None:
    """Test scheme can be set for relative file URL."""
    expected = URL("file:///absolute/path")
    assert expected.with_scheme("file") == expected


def test_with_scheme_invalid_type():
    url = URL("http://example.com")
    with pytest.raises(TypeError):
        assert str(url.with_scheme(123))


def test_with_user():
    url = URL("http://example.com")
    assert str(url.with_user("john")) == "http://john@example.com"


def test_with_user_non_ascii():
    url = URL("http://example.com")
    url2 = url.with_user("бажан")
    assert url2.raw_user == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD"
    assert url2.user == "бажан"
    assert url2.raw_authority == "%D0%B1%D0%B0%D0%B6%D0%B0%D0%BD@example.com"
    assert url2.authority == "бажан@example.com:80"


def test_with_user_percent_encoded():
    url = URL("http://example.com")
    url2 = url.with_user("%cf%80")
    assert url2.raw_user == "%25cf%2580"
    assert url2.user == "%cf%80"
    assert url2.raw_authority == "%25cf%2580@example.com"
    assert url2.authority == "%cf%80@example.com:80"


def test_with_user_for_relative_url():
    with pytest.raises(ValueError):
        URL("path/to").with_user("user")


def test_with_user_invalid_type():
    url = URL("http://example.com:123")
    with pytest.raises(TypeError):
        url.with_user(123)


def test_with_user_None():
    url = URL("http://john@example.com")
    assert str(url.with_user(None)) == "http://example.com"


def test_with_user_ipv6():
    url = URL("http://john:pass@[::1]:8080/")
    assert str(url.with_user(None)) == "http://[::1]:8080/"


def test_with_user_None_when_password_present():
    url = URL("http://john:pass@example.com")
    assert str(url.with_user(None)) == "http://example.com"


def test_with_password():
    url = URL("http://john@example.com")
    assert str(url.with_password("pass")) == "http://john:pass@example.com"


def test_with_password_ipv6():
    url = URL("http://john:pass@[::1]:8080/")
    assert str(url.with_password(None)) == "http://john@[::1]:8080/"


def test_with_password_non_ascii():
    url = URL("http://john@example.com")
    url2 = url.with_password("пароль")
    assert url2.raw_password == "%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C"
    assert url2.password == "пароль"
    assert url2.raw_authority == "john:%D0%BF%D0%B0%D1%80%D0%BE%D0%BB%D1%8C@example.com"
    assert url2.authority == "john:пароль@example.com:80"


def test_with_password_percent_encoded():
    url = URL("http://john@example.com")
    url2 = url.with_password("%cf%80")
    assert url2.raw_password == "%25cf%2580"
    assert url2.password == "%cf%80"
    assert url2.raw_authority == "john:%25cf%2580@example.com"
    assert url2.authority == "john:%cf%80@example.com:80"


def test_with_password_non_ascii_with_colon():
    url = URL("http://john@example.com")
    url2 = url.with_password("п:а")
    assert url2.raw_password == "%D0%BF%3A%D0%B0"
    assert url2.password == "п:а"


def test_with_password_for_relative_url():
    with pytest.raises(ValueError):
        URL("path/to").with_password("pass")


def test_with_password_None():
    url = URL("http://john:pass@example.com")
    assert str(url.with_password(None)) == "http://john@example.com"


def test_with_password_invalid_type():
    url = URL("http://example.com:123")
    with pytest.raises(TypeError):
        url.with_password(123)


def test_with_password_and_empty_user():
    url = URL("http://example.com")
    url2 = url.with_password("pass")
    assert url2.password == "pass"
    assert url2.user is None
    assert str(url2) == "http://:pass@example.com"


def test_from_str_with_host_ipv4():
    url = URL("http://host:80")
    url = url.with_host("192.168.1.1")
    assert url.raw_host == "192.168.1.1"


def test_from_str_with_host_ipv6():
    url = URL("http://host:80")
    url = url.with_host("::1")
    assert url.raw_host == "::1"


def test_with_host():
    url = URL("http://example.com:123")
    assert str(url.with_host("example.org")) == "http://example.org:123"


def test_with_host_empty():
    url = URL("http://example.com:123")
    with pytest.raises(ValueError):
        url.with_host("")


def test_with_host_non_ascii():
    url = URL("http://example.com:123")
    url2 = url.with_host("оун-упа.укр")
    assert url2.raw_host == "xn----8sb1bdhvc.xn--j1amh"
    assert url2.host == "оун-упа.укр"
    assert url2.raw_authority == "xn----8sb1bdhvc.xn--j1amh:123"
    assert url2.authority == "оун-упа.укр:123"


@pytest.mark.parametrize(
    ("host", "is_authority"),
    [
        ("user:pass@host.com", True),
        ("user@host.com", True),
        ("host:com", False),
        ("not_percent_encoded%Zf", False),
        ("still_not_percent_encoded%fZ", False),
        *(("other_gen_delim_" + c, False) for c in "/?#[]"),
    ],
)
def test_with_invalid_host(host: str, is_authority: bool):
    url = URL("http://example.com:123")
    match = r"Host '[^']+' cannot contain '[^']+' \(at position \d+\)"
    if is_authority:
        match += ", if .* use 'authority' instead of 'host'"
    with pytest.raises(ValueError, match=f"{match}$"):
        url.with_host(host=host)


def test_with_host_percent_encoded():
    url = URL("http://%25cf%2580%cf%80:%25cf%2580%cf%80@example.com:123")
    url2 = url.with_host("%cf%80.org")
    assert url2.raw_host == "%cf%80.org"
    assert url2.host == "%cf%80.org"
    assert url2.raw_authority == "%25cf%2580%CF%80:%25cf%2580%CF%80@%cf%80.org:123"
    assert url2.authority == "%cf%80π:%cf%80π@%cf%80.org:123"


def test_with_host_for_relative_url():
    with pytest.raises(ValueError):
        URL("path/to").with_host("example.com")


def test_with_host_invalid_type():
    url = URL("http://example.com:123")
    with pytest.raises(TypeError):
        url.with_host(None)


def test_with_port():
    url = URL("http://example.com")
    assert str(url.with_port(8888)) == "http://example.com:8888"


def test_with_default_port_normalization() -> None:
    url = URL("http://example.com")
    assert str(url.with_scheme("https")) == "https://example.com"
    assert str(url.with_scheme("https").with_port(443)) == "https://example.com"
    assert str(url.with_port(443).with_scheme("https")) == "https://example.com"


def test_with_custom_port_normalization() -> None:
    url = URL("http://example.com")
    u88 = url.with_port(88)
    assert str(u88) == "http://example.com:88"
    assert str(u88.with_port(80)) == "http://example.com"
    assert str(u88.with_scheme("https")) == "https://example.com:88"


def test_with_explicit_port_normalization() -> None:
    url = URL("http://example.com")
    u80 = url.with_port(80)
    assert str(u80) == "http://example.com"
    assert str(u80.with_port(81)) == "http://example.com:81"
    assert str(u80.with_scheme("https")) == "https://example.com:80"


def test_with_port_with_no_port():
    url = URL("http://example.com")
    assert str(url.with_port(None)) == "http://example.com"


def test_with_port_ipv6():
    url = URL("http://[::1]:8080/")
    assert str(url.with_port(81)) == "http://[::1]:81/"


def test_with_port_keeps_query_and_fragment():
    url = URL("http://example.com/?a=1#frag")
    assert str(url.with_port(8888)) == "http://example.com:8888/?a=1#frag"


def test_with_port_percent_encoded():
    url = URL("http://user%name:pass%word@example.com/")
    assert str(url.with_port(808)) == "http://user%25name:pass%25word@example.com:808/"


def test_with_port_for_relative_url():
    with pytest.raises(ValueError):
        URL("path/to").with_port(1234)


def test_with_port_invalid_type():
    with pytest.raises(TypeError):
        URL("http://example.com").with_port("123")
    with pytest.raises(TypeError):
        URL("http://example.com").with_port(True)


def test_with_port_invalid_range():
    with pytest.raises(ValueError):
        URL("http://example.com").with_port(-1)
