def signup(api, username):
    response = api.post("/api/v1/auth/signup", json={
        "username": username,
        "email": f"{username}@example.test", "password": "correct horse battery",
    })
    assert response.status_code == 201
    return response.json()


def token(api):
    response = api.post("/api/v1/auth/tokens", json={"label": "tests"})
    assert response.status_code == 201
    return response.json()["token"]


def project_body(slug="zip-safe", name="Safer ZIP"):
    return {"slug":slug,"name":name,"description":"Diversified ZIP reader",
            "repository_url":"https://github.com/example/zip-safe","spec_path":"specs/zip.toml"}


def release_body():
    return {"application":"local-name","version":"1.0.0","artifact_url":"https://example.test/zip-safe",
            "artifact_name":"zip-safe","artifact_sha256":"a"*64,"evidence_sha256":"b"*64,
            "signing_public_key":"public","signature":"signature","spec_functions":[
              {"name":"parse_header","implementations":[{"id":"parse_header.safe","name":"safe"},
                                                            {"id":"parse_header.second","name":"second"}]}]}


def test_signup_requires_email_and_uses_username_as_public_name(api):
    missing = api.post("/api/v1/auth/signup", json={
        "username": "ada", "password": "correct horse battery",
    })
    assert missing.status_code == 422

    user = signup(api, "ada")
    assert user["username"] == "ada"
    assert "display_name" not in user


def test_user_can_own_multiple_projects_and_public_pages_are_scoped(api):
    signup(api,"ada")
    first=api.post("/api/v1/projects",json=project_body()).json()
    second=api.post("/api/v1/projects",json=project_body("database-safe","Safer DB")).json()
    assert [item["slug"] for item in api.get("/api/v1/me/projects").json()] == [second["slug"],first["slug"]]
    assert api.get("/api/v1/projects/ada/zip-safe").json()["owner"] == "ada"
    profile = api.get("/api/v1/users/ada").json()
    assert "display_name" not in profile
    assert {item["slug"] for item in profile["projects"]} == {"zip-safe", "database-safe"}
    assert api.post("/api/v1/projects",json=project_body()).status_code == 409

    api.post("/api/v1/auth/logout")
    signup(api,"grace")
    same_slug=api.post("/api/v1/projects",json=project_body()).json()
    assert same_slug["owner"] == "grace"
    assert {item["owner"] for item in api.get("/api/v1/projects").json()} == {"ada","grace"}


def test_project_repository_is_required_and_owner_can_edit_details(api):
    signup(api, "ada")
    missing_repository = api.post("/api/v1/projects", json={
        "slug": "json-safe", "name": "Safer JSON", "description": "JSON parser",
    })
    assert missing_repository.status_code == 422

    created = api.post("/api/v1/projects", json={
        "slug": "json-safe", "name": "Safer JSON", "description": "JSON parser",
        "repository_url": "https://github.com/ada/json-safe",
    })
    assert created.status_code == 201
    assert created.json()["spec_path"] == "specs/polyform.toml"

    updated = api.put("/api/v1/projects/ada/json-safe", json={
        "name": "JSON Safe", "description": "Diversified JSON parser",
        "repository_url": "https://github.com/ada/json-safe",
        "spec_path": "specs/json.toml",
    })
    assert updated.status_code == 200
    assert updated.json()["name"] == "JSON Safe"
    assert updated.json()["spec_path"] == "specs/json.toml"

    api.post("/api/v1/auth/logout")
    signup(api, "outsider")
    denied = api.put("/api/v1/projects/ada/json-safe", json={
        "name": "Changed", "description": "",
        "repository_url": "https://github.com/ada/json-safe",
        "spec_path": "specs/polyform.toml",
    })
    assert denied.status_code == 422


def test_login_session_and_personal_token(api):
    signup(api,"linus")
    api.post("/api/v1/auth/logout")
    assert api.get("/api/v1/auth/me").status_code == 401
    assert api.post("/api/v1/auth/login",json={"username":"linus","password":"wrong password"}).status_code == 422
    login=api.post("/api/v1/auth/login",json={"username":"linus","password":"correct horse battery"})
    assert login.status_code == 200
    personal=token(api)
    api.post("/api/v1/auth/logout")
    assert api.get("/api/v1/me/projects",headers={"Authorization":f"Bearer {personal}"}).status_code == 200


def test_owner_publishes_scoped_release_and_operates_dashboard(api):
    signup(api,"margaret")
    api.post("/api/v1/projects",json=project_body())
    personal=token(api)
    headers={"Authorization":f"Bearer {personal}"}
    published=api.post("/api/v1/projects/margaret/zip-safe/releases",json=release_body(),headers=headers)
    assert published.status_code == 201
    release=published.json()
    assert release["application"] == "margaret/zip-safe"
    detail=api.get("/api/v1/projects/margaret/zip-safe").json()
    assert detail["releases"][0]["id"] == release["id"]
    assert detail["implementations"] == 2
    assert api.get("/api/v1/projects/margaret/zip-safe/catalog").json()["id"] == release["id"]
    status=api.put("/api/v1/implementations/parse_header.second/status",
                   params={"release_id":release["id"]},headers=headers,
                   json={"status":"quarantined","reason":"owner review"})
    assert status.status_code == 200

    api.post("/api/v1/auth/logout")
    signup(api,"outsider")
    outsider=token(api)
    denied=api.post("/api/v1/projects/margaret/zip-safe/releases",json={**release_body(),"version":"2.0.0"},
                    headers={"Authorization":f"Bearer {outsider}"})
    assert denied.status_code == 422
