"""§22 S5 — create-project vertical: a global Owner POSTs `/api/projects`, the
bot provisions a Gitea content repo + commits the project to `projects.yaml`, and
the registry mirror upserts the `projects` + default `collections` rows (registry
stays the source of truth). Plus the deployment-directory empty-state signals
(`viewer.can_create_project`, `default_project_readable`) that drive C3.1/C3.2.
"""
from __future__ import annotations

from fastapi.testclient import TestClient

from app import db
from test_propose_vertical import (  # noqa: F401
    app_with_fake_gitea, tmp_env, provision_user_row, sign_in_as,
)


def test_create_project_provisions_repo_commits_registry_and_mirrors(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben",
                   role="owner", email="ben@test")
        r = client.post("/api/projects",
                        json={"project_id": "acme", "name": "Acme", "type": "bdd",
                              "visibility": "public"})
        assert r.status_code == 200, r.text
        body = r.json()
        assert body["id"] == "acme"
        assert body["type"] == "bdd"
        assert body["visibility"] == "public"

        # The bot provisioned the content repo (default name <id>-content) and
        # seeded a README so `main` exists.
        assert ("wiggleverse", "acme-content") in fake.repos
        readme = fake.files.get(("wiggleverse", "acme-content", "main", "README.md"))
        assert readme is not None and "acme" in readme["content"]

        # The bot committed the new project into projects.yaml.
        reg = fake.files.get(("wiggleverse", "registry", "main", "projects.yaml"))
        assert reg is not None
        assert "id: acme" in reg["content"]
        assert "content_repo: acme-content" in reg["content"]

        # The registry refresh mirrored a projects row + its default collection.
        prow = db.conn().execute(
            "SELECT name, content_repo, visibility FROM projects WHERE id='acme'"
        ).fetchone()
        assert (prow["name"], prow["content_repo"], prow["visibility"]) == (
            "Acme", "acme-content", "public")
        crow = db.conn().execute(
            "SELECT type, project_id, subfolder FROM collections WHERE id='acme'"
        ).fetchone()
        assert (crow["type"], crow["project_id"], crow["subfolder"]) == ("bdd", "acme", "")

        # It is now visible in the deployment directory and readable.
        ids = {p["id"] for p in client.get("/api/deployment").json()["projects"]}
        assert "acme" in ids
        assert client.get("/api/projects/acme").status_code == 200

        # An audit row records the structural action with the global Owner actor.
        act = db.conn().execute(
            "SELECT actor_user_id FROM actions WHERE action_kind='create_project'"
        ).fetchone()
        assert act is not None and act["actor_user_id"] == 1


def test_create_project_custom_content_repo_name(app_with_fake_gitea):
    app, fake = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben",
                   role="owner", email="ben@test")
        r = client.post("/api/projects",
                        json={"project_id": "beta", "name": "Beta", "type": "document",
                              "content_repo": "beta-corpus"})
        assert r.status_code == 200, r.text
        assert ("wiggleverse", "beta-corpus") in fake.repos
        prow = db.conn().execute(
            "SELECT content_repo FROM projects WHERE id='beta'"
        ).fetchone()
        assert prow["content_repo"] == "beta-corpus"


def test_create_project_requires_global_owner(app_with_fake_gitea):
    # A plain deployment contributor is not a global Owner (C: + New project is a
    # global-Owner action), even though they may create collections.
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=2, login="alice", role="contributor")
        sign_in_as(client, user_id=2, gitea_login="alice", display_name="Alice",
                   role="contributor", email="alice@test")
        r = client.post("/api/projects",
                        json={"project_id": "x", "name": "X", "type": "bdd"})
        assert r.status_code == 403


def test_create_project_global_owner_grant_permitted(app_with_fake_gitea):
    # An explicit global-scope Owner grant (not a deployment owner/admin) may
    # create projects.
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=3, login="gina", role="contributor")
        db.conn().execute(
            "INSERT INTO memberships (scope_type, scope_id, user_id, role) "
            "VALUES ('global', '*', 3, 'owner')"
        )
        sign_in_as(client, user_id=3, gitea_login="gina", display_name="Gina",
                   role="contributor", email="gina@test")
        r = client.post("/api/projects",
                        json={"project_id": "gproj", "name": "G", "type": "document"})
        assert r.status_code == 200, r.text


def test_create_project_anonymous_rejected(app_with_fake_gitea):
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        r = client.post("/api/projects",
                        json={"project_id": "x", "name": "X", "type": "bdd"})
        assert r.status_code in (401, 403)


def test_create_project_rejects_duplicate(app_with_fake_gitea):
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben",
                   role="owner", email="ben@test")
        ok = client.post("/api/projects",
                         json={"project_id": "acme", "name": "Acme", "type": "bdd"})
        assert ok.status_code == 200, ok.text
        dup = client.post("/api/projects",
                          json={"project_id": "acme", "name": "Acme 2", "type": "bdd"})
        assert dup.status_code == 409


def test_create_project_rejects_reserved_default_id(app_with_fake_gitea):
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben",
                   role="owner", email="ben@test")
        r = client.post("/api/projects",
                        json={"project_id": "default", "name": "X", "type": "bdd"})
        assert r.status_code == 422


def test_create_project_rejects_bad_type(app_with_fake_gitea):
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben",
                   role="owner", email="ben@test")
        r = client.post("/api/projects",
                        json={"project_id": "x", "name": "X", "type": "nonsense"})
        assert r.status_code == 422


# --- C3.1 / C3.2: deployment-directory empty-state signals ------------------


def test_deployment_owner_sees_create_project_capability(app_with_fake_gitea):
    # C3.1: a global Owner is offered the create-project action.
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=1, login="ben", role="owner")
        sign_in_as(client, user_id=1, gitea_login="ben", display_name="Ben", role="owner")
        body = client.get("/api/deployment").json()
        assert body["viewer"]["can_create_project"] is True


def test_deployment_non_owner_no_create_capability(app_with_fake_gitea):
    # C3.2: a granted account with no roles is not offered create-project.
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        provision_user_row(user_id=2, login="vee", role="contributor")
        sign_in_as(client, user_id=2, gitea_login="vee", display_name="Vee", role="contributor")
        body = client.get("/api/deployment").json()
        assert body["viewer"]["can_create_project"] is False


def test_deployment_default_readable_when_default_is_public(app_with_fake_gitea):
    # The seeded default project is public → readable → the N=1 redirect target
    # is valid (land-in-corpus preserved).
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        body = client.get("/api/deployment").json()
        assert body["default_project_readable"] is True


def test_deployment_default_not_readable_when_only_gated(app_with_fake_gitea):
    # C3.2: the only project is gated; a granted non-member sees no visible
    # projects AND default_project_readable False → the frontend renders the
    # empty directory (no 404 bounce).
    app, _ = app_with_fake_gitea
    with TestClient(app) as client:
        db.conn().execute("UPDATE projects SET visibility='gated' WHERE id='default'")
        db.conn().execute("UPDATE collections SET visibility='gated' WHERE id='default'")
        provision_user_row(user_id=2, login="vee", role="contributor")
        sign_in_as(client, user_id=2, gitea_login="vee", display_name="Vee", role="contributor")
        body = client.get("/api/deployment").json()
        assert body["projects"] == []
        assert body["default_project_readable"] is False
