"""The kit wired to DFP: the owner sentinel, `can()`, the View As policy, `/api/bw/me`.

With ACCOUNT_OWNER unset or a placeholder nobody is owner (`is_owner("rian")` is false and
so is the sentinel itself), because the kit would otherwise default one silently; a consumer
with no member row holds nothing; the owner may view as an active account that holds no
member row and never as the owner or a disabled one; `/api/bw/me` carries DFP's
`capabilities` beside the kit's booleans, all false for an anonymous caller; the store
refuses a disabled principal with DISABLED and a duplicate level with EXISTS. On SQLite.
"""

import pytest
from sqlalchemy import select

from app.config import settings
from app.models import AccountLevel, AuditLog, AuthSession
from app.services import accounts, view_as
from app.vendor import bw_accounts as bwa
from tests import _accounts as T
from tests.kit import _env


@pytest.fixture
def world(monkeypatch):
    T.fresh(monkeypatch)
    T.person("rian")
    with _env.TestSessionLocal() as db:
        db.add(AccountLevel(name="admin", permissions=list(accounts.SEED_LEVELS["admin"]["permissions"]), assignable=[]))
        db.commit()
    T.person("adam", level="admin")
    T.person("consumer")
    T.person("gone", status="disabled")
    yield


class TestOwner:
    @pytest.mark.parametrize("value", ["", "REPLACE_WITH_USERNAME", "Not A Username"])
    def test_unset_or_placeholder_means_nobody_is_owner(self, world, monkeypatch, value):
        monkeypatch.setattr(settings, "account_owner", value)
        assert accounts.init() == accounts.NO_OWNER
        assert accounts.is_owner("rian") is False
        assert accounts.is_owner(accounts.NO_OWNER) is False
        assert accounts.can("rian", accounts.PERM_PLAN_VIEW) is False

    def test_the_named_owner_holds_everything(self, world):
        assert accounts.is_owner("rian") and accounts.is_owner("  RIAN ")
        assert all(accounts.can("rian", p) for p in accounts.DFP_PERMISSIONS)
        assert accounts.can("rian", bwa.PERM_LEVELS_EDIT)


class TestCan:
    def test_a_consumer_with_no_member_row_holds_nothing(self, world):
        assert accounts.can("consumer", accounts.PERM_CLIENT_VIEW) is False
        assert accounts.can(None, accounts.PERM_CLIENT_VIEW) is False

    def test_an_admin_holds_the_client_pages_and_not_the_plan(self, world):
        assert accounts.can("adam", accounts.PERM_CLIENT_VIEW)
        assert accounts.can("adam", accounts.PERM_CLIENT_PARTICIPATE)
        assert accounts.can("adam", accounts.PERM_PLAN_VIEW) is False
        assert accounts.can("adam", bwa.PERM_ACCOUNTS_VIEW_AS) is False

    def test_the_six_permissions_are_registered_as_enforced(self, world):
        known = {p["name"]: p for p in bwa.known_permissions()}
        for perm in accounts.DFP_PERMISSIONS:
            assert known[perm]["enforced"] is True, perm


class TestViewAsPolicy:
    def test_owner_may_view_as_an_active_non_member(self, world):
        assert view_as.can_view_as("rian", "consumer")
        assert view_as.target_valid("consumer")

    def test_never_the_owner_nor_a_disabled_or_unknown_account(self, world):
        for name in ("rian", "gone", "nobody", ""):
            assert view_as.target_valid(name) is False, name

    def test_act_mode_is_the_owners_alone_and_read_only_needs_the_permission(self, world):
        c = T.client()
        T.as_user(c, "adam")
        r = c.post("/api/bw/view-as/start", json={"target": "consumer"})
        assert r.status_code == 403  # admin holds no accounts.view_as
        # The owner starts read-only on a non-member, then act; both survive a request.
        T.as_user(c, "rian")
        assert c.post("/api/bw/view-as/start", json={"target": "consumer"}).status_code == 200
        me = c.get("/api/bw/me").json()
        assert me["impersonating"] and me["viewing_as"] == "consumer" and me["can_write"] is False
        assert c.get("/api/whoami").json() == {"username": "consumer", "real_user": "rian", "is_owner": False}
        with _env.TestSessionLocal() as db:
            row = db.scalar(select(AuthSession).where(AuthSession.acting_as.isnot(None)))
            assert row.acting_as == "consumer" and row.acting_mode == "readonly"
        assert c.post("/api/bw/view-as/stop").status_code == 200
        assert c.post("/api/bw/view-as/start", json={"target": "consumer", "mode": "act"}).status_code == 200
        assert c.get("/api/bw/me").json()["can_write"] is True
        c.post("/api/bw/view-as/stop")
        assert T.audit_actions().count("view_as.start") == 2 and T.audit_actions().count("view_as.stop") == 2

    def test_a_target_that_is_disabled_mid_session_auto_stops_with_one_audit_row(self, world):
        c = T.client()
        T.as_user(c, "rian")
        c.post("/api/bw/view-as/start", json={"target": "consumer"})
        from app.services import directory

        directory.disable_account("consumer", via="test")
        assert c.get("/api/whoami").json()["username"] == "rian"
        with _env.TestSessionLocal() as db:
            rows = db.scalars(select(AuditLog).where(AuditLog.action == "view_as.stop")).all()
            assert len(rows) == 1 and rows[0].detail == {"real": "rian", "mode": "readonly", "auto": True}
            assert db.scalar(select(AuthSession)).acting_as is None


class TestMe:
    def test_anonymous_has_the_block_all_false(self, world):
        j = T.client().get("/api/bw/me").json()
        assert j["authenticated"] is False
        assert j["capabilities"] == {k: False for k in (
            "client_view", "client_participate", "discussion_curate", "plan_view", "items_act",
            "sources_manage", "must_change_password")}
        assert j["display_name"] is None

    def test_owner_and_admin_blocks(self, world):
        c = T.client()
        T.as_user(c, "rian")
        j = c.get("/api/bw/me").json()
        assert j["is_owner"] and j["can_view_as_others"] and j["display_name"] == "Rian"
        assert all(j["capabilities"][k] for k in ("client_view", "plan_view", "items_act", "sources_manage", "discussion_curate"))
        assert j["capabilities"]["must_change_password"] is False
        T.as_user(c, "adam")
        j = c.get("/api/bw/me").json()
        assert j["is_owner"] is False and j["can_manage_accounts"] is False
        assert j["capabilities"]["client_view"] and j["capabilities"]["client_participate"]
        assert not j["capabilities"]["plan_view"] and not j["capabilities"]["sources_manage"]

    def test_must_change_rides_with_the_real_account(self, world):
        T.person("fresh", must_change=True)
        c = T.client()
        T.as_user(c, "fresh")
        assert c.get("/api/bw/me").json()["capabilities"]["must_change_password"] is True


class TestStore:
    def test_a_disabled_principal_cannot_be_added_and_a_level_is_created_once(self, world):
        with pytest.raises(accounts.AccountsError) as exc:
            bwa.add_member("rian", "gone", "admin")
        assert exc.value.code == "DISABLED"
        with pytest.raises(accounts.AccountsError) as exc:
            bwa.add_member("rian", "nobody", "admin")
        assert exc.value.code == "NO_SUCH_MEMBER"
        with pytest.raises(accounts.AccountsError) as exc:
            accounts.STORE.add_level("admin", [], [])
        assert exc.value.code == "EXISTS"
