# Copyright (C) 2015-2019  The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information

from hypothesis import given

from swh.web.api.utils import enrich_revision
from swh.web.common.exc import NotFoundExc
from swh.web.common.utils import reverse
from swh.web.tests.data import random_sha1
from swh.web.tests.strategies import revision


@given(revision())
def test_api_revision(api_client, archive_data, revision):
    url = reverse("api-1-revision", url_args={"sha1_git": revision})
    rv = api_client.get(url)

    expected_revision = archive_data.revision_get(revision)

    enrich_revision(expected_revision, rv.wsgi_request)

    assert rv.status_code == 200, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == expected_revision


def test_api_revision_not_found(api_client):
    unknown_revision_ = random_sha1()

    url = reverse("api-1-revision", url_args={"sha1_git": unknown_revision_})
    rv = api_client.get(url)

    assert rv.status_code == 404, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == {
        "exception": "NotFoundExc",
        "reason": "Revision with sha1_git %s not found." % unknown_revision_,
    }


@given(revision())
def test_api_revision_raw_ok(api_client, archive_data, revision):
    url = reverse("api-1-revision-raw-message", url_args={"sha1_git": revision})
    rv = api_client.get(url)

    expected_message = archive_data.revision_get(revision)["message"]

    assert rv.status_code == 200
    assert rv["Content-Type"] == "application/octet-stream"
    assert rv.content == expected_message.encode()


def test_api_revision_raw_ko_no_rev(api_client):
    unknown_revision_ = random_sha1()

    url = reverse(
        "api-1-revision-raw-message", url_args={"sha1_git": unknown_revision_}
    )
    rv = api_client.get(url)

    assert rv.status_code == 404, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == {
        "exception": "NotFoundExc",
        "reason": "Revision with sha1_git %s not found." % unknown_revision_,
    }


@given(revision())
def test_api_revision_log(api_client, archive_data, revision):
    per_page = 10

    url = reverse(
        "api-1-revision-log",
        url_args={"sha1_git": revision},
        query_params={"per_page": per_page},
    )

    rv = api_client.get(url)

    expected_log = archive_data.revision_log(revision, limit=per_page + 1)
    expected_log = list(
        map(enrich_revision, expected_log, [rv.wsgi_request] * len(expected_log))
    )

    has_next = len(expected_log) > per_page

    assert rv.status_code == 200, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == (expected_log[:-1] if has_next else expected_log)

    if has_next:
        assert "Link" in rv
        next_log_url = rv.wsgi_request.build_absolute_uri(
            reverse(
                "api-1-revision-log",
                url_args={"sha1_git": expected_log[-1]["id"]},
                query_params={"per_page": per_page},
            )
        )
        assert next_log_url in rv["Link"]


def test_api_revision_log_not_found(api_client):
    unknown_revision_ = random_sha1()

    url = reverse("api-1-revision-log", url_args={"sha1_git": unknown_revision_})

    rv = api_client.get(url)

    assert rv.status_code == 404, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == {
        "exception": "NotFoundExc",
        "reason": "Revision with sha1_git %s not found." % unknown_revision_,
    }
    assert not rv.has_header("Link")


@given(revision())
def test_api_revision_log_context(api_client, archive_data, revision):
    revisions = archive_data.revision_log(revision, limit=4)

    prev_rev = revisions[0]["id"]
    rev = revisions[-1]["id"]

    per_page = 10

    url = reverse(
        "api-1-revision-log",
        url_args={"sha1_git": rev, "prev_sha1s": prev_rev},
        query_params={"per_page": per_page},
    )

    rv = api_client.get(url)

    expected_log = archive_data.revision_log(rev, limit=per_page)
    prev_revision = archive_data.revision_get(prev_rev)
    expected_log.insert(0, prev_revision)
    expected_log = list(
        map(enrich_revision, expected_log, [rv.wsgi_request] * len(expected_log))
    )

    assert rv.status_code == 200, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == expected_log


def test_api_revision_directory_ko_not_found(api_client, mocker):
    mock_rev_dir = mocker.patch("swh.web.api.views.revision._revision_directory_by")
    mock_rev_dir.side_effect = NotFoundExc("Not found")

    rv = api_client.get("/api/1/revision/999/directory/some/path/to/dir/")

    assert rv.status_code == 404, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == {"exception": "NotFoundExc", "reason": "Not found"}

    mock_rev_dir.assert_called_once_with(
        {"sha1_git": "999"},
        "some/path/to/dir",
        "/api/1/revision/999/directory/some/path/to/dir/",
        with_data=False,
    )


def test_api_revision_directory_ok_returns_dir_entries(api_client, mocker):
    mock_rev_dir = mocker.patch("swh.web.api.views.revision._revision_directory_by")
    stub_dir = {
        "type": "dir",
        "revision": "999",
        "content": [
            {
                "sha1_git": "789",
                "type": "file",
                "target": "101",
                "target_url": "/api/1/content/sha1_git:101/",
                "name": "somefile",
                "file_url": "/api/1/revision/999/directory/some/path/" "somefile/",
            },
            {
                "sha1_git": "123",
                "type": "dir",
                "target": "456",
                "target_url": "/api/1/directory/456/",
                "name": "to-subdir",
                "dir_url": "/api/1/revision/999/directory/some/path/" "to-subdir/",
            },
        ],
    }

    mock_rev_dir.return_value = stub_dir

    rv = api_client.get("/api/1/revision/999/directory/some/path/")

    stub_dir["content"][0]["target_url"] = rv.wsgi_request.build_absolute_uri(
        stub_dir["content"][0]["target_url"]
    )
    stub_dir["content"][0]["file_url"] = rv.wsgi_request.build_absolute_uri(
        stub_dir["content"][0]["file_url"]
    )
    stub_dir["content"][1]["target_url"] = rv.wsgi_request.build_absolute_uri(
        stub_dir["content"][1]["target_url"]
    )
    stub_dir["content"][1]["dir_url"] = rv.wsgi_request.build_absolute_uri(
        stub_dir["content"][1]["dir_url"]
    )

    assert rv.status_code == 200, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == stub_dir

    mock_rev_dir.assert_called_once_with(
        {"sha1_git": "999"},
        "some/path",
        "/api/1/revision/999/directory/some/path/",
        with_data=False,
    )


def test_api_revision_directory_ok_returns_content(api_client, mocker):
    mock_rev_dir = mocker.patch("swh.web.api.views.revision._revision_directory_by")
    stub_content = {
        "type": "file",
        "revision": "999",
        "content": {
            "sha1_git": "789",
            "sha1": "101",
            "data_url": "/api/1/content/101/raw/",
        },
    }

    mock_rev_dir.return_value = stub_content

    url = "/api/1/revision/666/directory/some/other/path/"
    rv = api_client.get(url)

    stub_content["content"]["data_url"] = rv.wsgi_request.build_absolute_uri(
        stub_content["content"]["data_url"]
    )

    assert rv.status_code == 200, rv.data
    assert rv["Content-Type"] == "application/json"
    assert rv.data == stub_content

    mock_rev_dir.assert_called_once_with(
        {"sha1_git": "666"}, "some/other/path", url, with_data=False
    )


@given(revision())
def test_api_revision_uppercase(api_client, revision):
    url = reverse(
        "api-1-revision-uppercase-checksum", url_args={"sha1_git": revision.upper()}
    )

    resp = api_client.get(url)
    assert resp.status_code == 302

    redirect_url = reverse("api-1-revision", url_args={"sha1_git": revision})

    assert resp["location"] == redirect_url
