"""Test main handlers"""

import time
from urllib.parse import quote, urlparse

import jwt
import pytest
from bs4 import BeautifulSoup

from binderhub import __version__ as binder_version

from .utils import async_requests


@pytest.mark.parametrize(
    "old_url, new_url",
    [
        (
            "/repo/binderhub-ci-repos/requirements",
            "/v2/gh/binderhub-ci-repos/requirements/master",
        ),
        (
            "/repo/binderhub-ci-repos/requirements/",
            "/v2/gh/binderhub-ci-repos/requirements/master",
        ),
        (
            "/repo/binderhub-ci-repos/requirements/notebooks/index.ipynb",
            "/v2/gh/binderhub-ci-repos/requirements/master?urlpath=%2Fnotebooks%2Findex.ipynb",
        ),
    ],
)
async def test_legacy_redirect(app, old_url, new_url):
    r = await async_requests.get(app.url + old_url, allow_redirects=False)
    assert r.status_code == 302
    assert r.headers["location"] == new_url


def _resolve_url(page_url, url):
    """Resolve a URL relative to a page"""

    # full URL, nothing to resolve
    if "://" in url:
        return url

    parsed = urlparse(page_url)

    if url.startswith("/"):
        # absolute path
        return f"{parsed.scheme}://{parsed.netloc}{url}"

    # relative path URL

    if page_url.endswith("/"):
        # URL is a directory, resolve relative to dir
        path = parsed.path
    else:
        # URL is not a directory, resolve relative to parent
        path = parsed.path.rsplit("/", 1)[0] + "/"

    return f"{parsed.scheme}://{parsed.netloc}{path}{url}"


@pytest.mark.remote
async def test_main_page(app):
    """Check the main page and any links on it"""
    r = await async_requests.get(app.url)
    assert r.status_code == 200
    soup = BeautifulSoup(r.text, "html5lib")

    # check src links (style, images)
    for el in soup.find_all(src=True):
        url = _resolve_url(app.url, el["src"])
        r = await async_requests.get(url)
        assert r.status_code == 200, f"{r.status_code} {url}"

    # check hrefs
    for el in soup.find_all(href=True):
        href = el["href"]
        if href.startswith("#"):
            continue
        url = _resolve_url(app.url, href)
        r = await async_requests.get(url)
        assert r.status_code == 200, f"{r.status_code} {url}"


@pytest.mark.remote
@pytest.mark.helm
async def test_custom_template(app):
    """Check that our custom template config is applied via the helm chart"""
    r = await async_requests.get(app.url)
    assert r.status_code == 200
    assert "test-template" in r.text


@pytest.mark.remote
async def test_about_handler(app):
    # Check that the about page loads
    r = await async_requests.get(app.url + "/about")
    assert r.status_code == 200
    assert "This website is powered by" in r.text
    assert binder_version.split("+")[0] in r.text


@pytest.mark.remote
async def test_versions_handler(app):
    # Check that the about page loads
    r = await async_requests.get(app.url + "/versions")
    assert r.status_code == 200

    data = r.json()
    # builder_info is different for KubernetesExecutor and LocalRepo2dockerBuild
    try:
        import repo2docker

        allowed_builder_info = [{"repo2docker-version": repo2docker.__version__}]
    except ImportError:
        allowed_builder_info = []
    allowed_builder_info.append({"build_image": app.build_image})

    assert data["builder_info"] in allowed_builder_info
    assert data["binderhub"].split("+")[0] == binder_version.split("+")[0]


@pytest.mark.parametrize(
    "provider_prefix,repo,ref,path,path_type,status_code",
    [
        ("gh", "binderhub-ci-repos/requirements", "master", "", "", 200),
        ("gh", "binderhub-ci-repos%2Frequirements", "master", "", "", 400),
        ("gh", "binderhub-ci-repos/requirements", "master/", "", "", 200),
        (
            "gh",
            "binderhub-ci-repos/requirements",
            "20c4fe55a9b2c5011d228545e821b1c7b1723652",
            "index.ipynb",
            "file",
            200,
        ),
        (
            "gh",
            "binderhub-ci-repos/requirements",
            "20c4fe55a9b2c5011d228545e821b1c7b1723652",
            "%2Fnotebooks%2Findex.ipynb",
            "url",
            200,
        ),
        ("gh", "binderhub-ci-repos/requirements", "master", "has%20space", "file", 200),
        (
            "gh",
            "binderhub-ci-repos/requirements",
            "master/",
            "%2Fhas%20space%2F",
            "file",
            200,
        ),
        (
            "gh",
            "binderhub-ci-repos/requirements",
            "master",
            "%2Fhas%20space%2F%C3%BCnicode.ipynb",
            "file",
            200,
        ),
    ],
)
async def test_loading_page(
    app, provider_prefix, repo, ref, path, path_type, status_code
):
    # repo = f'{org}/{repo_name}'
    spec = f"{repo}/{ref}"
    provider_spec = f"{provider_prefix}/{spec}"
    query = f"{path_type}path={path}" if path else ""
    uri = f"/v2/{provider_spec}?{query}"
    r = await async_requests.get(app.url + uri)
    assert r.status_code == status_code, f"{r.status_code} {uri}"
    if status_code == 200:
        soup = BeautifulSoup(r.text, "html5lib")
        assert soup.find(id="log-container")
        nbviewer_url = soup.find(id="nbviewer-preview").find("iframe").attrs["src"]
        r = await async_requests.get(nbviewer_url)
        assert r.status_code == 200, f"{r.status_code} {nbviewer_url}"


@pytest.mark.parametrize(
    "origin,host,expected_origin",
    [
        ("https://my.host", "my.host", "my.host"),
        ("https://my.origin", "my.host", "my.origin"),
        (None, "my.host", "my.host"),
    ],
)
async def test_build_token_origin(app, origin, host, expected_origin):
    provider_spec = "git/{}/HEAD".format(
        quote(
            "https://github.com/binderhub-ci-repos/cached-minimal-dockerfile",
            safe="",
        )
    )
    uri = f"/v2/{provider_spec}"
    headers = {}
    if origin:
        headers["Origin"] = origin
    if host:
        headers["Host"] = host

    r = await async_requests.get(app.url + uri, headers=headers)

    soup = BeautifulSoup(r.text, "html5lib")
    assert soup.find(id="build-token")
    token_element = soup.find(id="build-token")
    assert token_element
    assert "data-token" in token_element.attrs
    build_token = token_element["data-token"]
    payload = jwt.decode(
        build_token,
        audience=provider_spec,
        options=dict(verify_signature=False),
    )
    assert payload["aud"] == provider_spec
    assert payload["origin"] == expected_origin
    assert time.time() < payload["exp"] < time.time() + 7200
