# Copyright (c) 2015 Ian Stapleton Cordasco
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pytest

import rfc3986
from . import base
from rfc3986 import exceptions
from rfc3986 import parseresult as pr

INVALID_PORTS = ["443:80", "443:80:443", "abcdef", "port", "43port"]

SNOWMAN = b"\xe2\x98\x83"
SNOWMAN_IDNA_HOST = "http://xn--n3h.com"


@pytest.mark.parametrize("port", INVALID_PORTS)
def test_port_parsing(port):
    with pytest.raises(exceptions.InvalidPort):
        rfc3986.urlparse(f"https://httpbin.org:{port}/get")


@pytest.mark.parametrize(
    "parts, unsplit",
    [
        (("https", None, "httpbin.org"), "https://httpbin.org"),
        (("https", "user", "httpbin.org"), "https://user@httpbin.org"),
        (
            ("https", None, "httpbin.org", 443, "/get"),
            "https://httpbin.org:443/get",
        ),
        (("HTTPS", None, "HTTPBIN.ORG"), "https://httpbin.org"),
    ],
)
def test_from_parts(parts, unsplit):
    uri = pr.ParseResult.from_parts(*parts)
    assert uri.unsplit() == unsplit


@pytest.mark.parametrize(
    "parts, unsplit",
    [
        (("https", None, "httpbin.org"), b"https://httpbin.org"),
        (("https", "user", "httpbin.org"), b"https://user@httpbin.org"),
        (
            ("https", None, "httpbin.org", 443, "/get"),
            b"https://httpbin.org:443/get",
        ),
        (("HTTPS", None, "HTTPBIN.ORG"), b"https://httpbin.org"),
    ],
)
def test_bytes_from_parts(parts, unsplit):
    uri = pr.ParseResultBytes.from_parts(*parts)
    assert uri.unsplit() == unsplit


class TestParseResultParsesURIs(base.BaseTestParsesURIs):
    test_class = pr.ParseResult


class TestParseResultUnsplits(base.BaseTestUnsplits):
    test_class = pr.ParseResult


def test_normalizes_uris_when_using_from_string(uri_to_normalize):
    """Verify we always get the same thing out as we expect."""
    result = pr.ParseResult.from_string(uri_to_normalize, lazy_normalize=False)
    assert result.scheme == "https"
    assert result.host == "example.com"


class TestStdlibShims:
    def test_uri_with_everything(self, uri_with_everything):
        uri = pr.ParseResult.from_string(uri_with_everything)
        assert uri.host == uri.hostname
        assert uri.netloc == uri.authority
        assert uri.query == uri.params
        assert uri.geturl() == uri.unsplit()


def test_creates_a_copy_with_a_new_path(uri_with_everything):
    uri = pr.ParseResult.from_string(uri_with_everything)
    new_uri = uri.copy_with(path="/parse/result/tests/are/fun")
    assert new_uri.path == "/parse/result/tests/are/fun"


def test_creates_a_copy_with_a_new_port(basic_uri):
    uri = pr.ParseResult.from_string(basic_uri)
    new_uri = uri.copy_with(port=443)
    assert new_uri.port == 443


def test_parse_result_encodes_itself(uri_with_everything):
    uri = pr.ParseResult.from_string(uri_with_everything)
    uribytes = uri.encode()
    encoding = uri.encoding
    assert uri.scheme.encode(encoding) == uribytes.scheme
    assert uri.userinfo.encode(encoding) == uribytes.userinfo
    assert uri.host.encode(encoding) == uribytes.host
    assert uri.port == uribytes.port
    assert uri.path.encode(encoding) == uribytes.path
    assert uri.query.encode(encoding) == uribytes.query
    assert uri.fragment.encode(encoding) == uribytes.fragment


class TestParseResultBytes:
    def test_handles_uri_with_everything(self, uri_with_everything):
        uri = pr.ParseResultBytes.from_string(uri_with_everything)
        assert uri.scheme == b"https"
        assert uri.path == b"/path/to/resource"
        assert uri.query == b"key=value"
        assert uri.fragment == b"fragment"
        assert uri.userinfo == b"user:pass"
        assert uri.port == 443
        assert isinstance(uri.authority, bytes) is True

    def test_raises_invalid_authority_for_invalid_uris(self, invalid_uri):
        with pytest.raises(exceptions.InvalidAuthority):
            pr.ParseResultBytes.from_string(invalid_uri)

    @pytest.mark.parametrize("port", INVALID_PORTS)
    def test_raises_invalid_port_non_strict_parse(self, port):
        with pytest.raises(exceptions.InvalidPort):
            pr.ParseResultBytes.from_string(
                f"https://httpbin.org:{port}/get", strict=False
            )

    def test_copy_with_a_new_path(self, uri_with_everything):
        uri = pr.ParseResultBytes.from_string(uri_with_everything)
        new_uri = uri.copy_with(path=b"/parse/result/tests/are/fun")
        assert new_uri.path == b"/parse/result/tests/are/fun"

    def test_copy_with_a_new_unicode_path(self, uri_with_everything):
        uri = pr.ParseResultBytes.from_string(uri_with_everything)
        pathbytes = b"/parse/result/tests/are/fun" + SNOWMAN
        new_uri = uri.copy_with(path=pathbytes.decode("utf-8"))
        assert new_uri.path == (b"/parse/result/tests/are/fun" + SNOWMAN)

    def test_unsplit(self):
        uri = pr.ParseResultBytes.from_string(
            b"http://" + SNOWMAN + b".com/path", strict=False
        )
        idna_encoded = SNOWMAN_IDNA_HOST.encode("utf-8") + b"/path"
        assert uri.unsplit(use_idna=True) == idna_encoded

    def test_eager_normalization_from_string(self):
        uri = pr.ParseResultBytes.from_string(
            b"http://" + SNOWMAN + b".com/path",
            strict=False,
            lazy_normalize=False,
        )
        assert uri.unsplit() == b"http:/path"

    def test_eager_normalization_from_parts(self):
        uri = pr.ParseResultBytes.from_parts(
            scheme="http",
            host=SNOWMAN.decode("utf-8"),
            path="/path",
            lazy_normalize=False,
        )
        assert uri.unsplit() == b"http:/path"
