"""Authorship comes from the session, never the payload (accounts plan §4.8).

A comment posted as adam by rian in act mode is adam's row (author_id and the display-name
snapshot) with one act_as.write row naming rian; an admin holder cannot edit another's
comment; an admin holder's post with an `item:*` key and edit of an `item:*` comment both
answer 404, the list omits `item:*` rows for them and returns them for the owner; a payload
`author` is ignored everywhere. In-process on SQLite; the comment routes write real rows.
"""

import pytest
from sqlalchemy import select

from app.models import Account, AccountLevel, AuditLog, DiscussionComment
from app.services import accounts
from tests import _accounts as T
from tests.kit import _env


@pytest.fixture
def world(monkeypatch):
    T.fresh(monkeypatch)
    T.person("rian", display_name="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", display_name="Adam")
    T.person("mark", level="admin", display_name="Mark")
    yield


def ids() -> dict[str, int]:
    with _env.TestSessionLocal() as db:
        return {a.username: a.id for a in db.scalars(select(Account))}


class TestFromTheSession:
    def test_a_comment_is_the_signed_in_accounts_whatever_the_payload_says(self, world):
        c = T.client()
        T.as_user(c, "adam")
        r = c.post("/api/discussion/feature-comments", json={"feature_key": "structure:urls", "author": "Someone Else", "body": "hello"})
        assert r.status_code == 201, r.text
        assert r.json()["author"] == "Adam"
        with _env.TestSessionLocal() as db:
            row = db.scalar(select(DiscussionComment))
        assert row.author == "Adam" and row.author_id == ids()["adam"]

    def test_act_mode_writes_the_targets_row_and_one_row_naming_rian(self, world):
        c = T.client()
        T.as_user(c, "rian")
        assert c.post("/api/bw/view-as/start", json={"target": "adam", "mode": "act"}).status_code == 200
        r = c.post("/api/discussion/feature-comments", json={"feature_key": "quote:core", "body": "as adam"})
        assert r.status_code == 201 and r.json()["author"] == "Adam"
        with _env.TestSessionLocal() as db:
            row = db.scalar(select(DiscussionComment))
            trail = db.scalars(select(AuditLog).where(AuditLog.action == "act_as.write")).all()
        assert row.author_id == ids()["adam"]
        assert len(trail) == 1 and trail[0].account_id == ids()["rian"] and trail[0].acting_as_id == ids()["adam"]


class TestEditRules:
    def _post_as(self, username: str, key: str = "structure:urls") -> int:
        c = T.client()
        T.as_user(c, username)
        r = c.post("/api/discussion/feature-comments", json={"feature_key": key, "body": f"by {username}"})
        assert r.status_code == 201, r.text
        return r.json()["id"]

    def test_an_admin_edits_own_but_not_anothers(self, world):
        marks = self._post_as("mark")
        adams = self._post_as("adam")
        c = T.client()
        T.as_user(c, "adam")
        assert c.post(f"/api/discussion/feature-comments/{adams}", json={"body": "adam again"}).status_code == 200
        r = c.post(f"/api/discussion/feature-comments/{marks}", json={"body": "not mine"})
        assert r.status_code == 403 and r.json()["detail"]["error_code"] == "FORBIDDEN"
        # The owner (discussion.curate by absence) edits anyone's.
        T.as_user(c, "rian")
        assert c.post(f"/api/discussion/feature-comments/{marks}", json={"body": "curated"}).status_code == 200
        with _env.TestSessionLocal() as db:
            assert db.get(DiscussionComment, marks).author == "Mark"  # the author never moves


class TestItemThreads:
    def test_item_threads_are_the_owners_by_absence(self, world):
        c = T.client()
        T.as_user(c, "rian")
        r = c.post("/api/discussion/feature-comments", json={"feature_key": "item:decide-x", "body": "the plan"})
        assert r.status_code == 201
        item_comment = r.json()["id"]
        c.post("/api/discussion/feature-comments", json={"feature_key": "structure:urls", "body": "public"})
        assert {x["feature_key"] for x in c.get("/api/discussion/feature-comments").json()} == {"item:decide-x", "structure:urls"}
        T.as_user(c, "adam")
        assert [x["feature_key"] for x in c.get("/api/discussion/feature-comments").json()] == ["structure:urls"]
        r = c.post("/api/discussion/feature-comments", json={"feature_key": "item:decide-x", "body": "peek"})
        assert r.status_code == 404
        r = c.post(f"/api/discussion/feature-comments/{item_comment}", json={"body": "peek"})
        assert r.status_code == 404
        with _env.TestSessionLocal() as db:
            assert db.get(DiscussionComment, item_comment).body == "the plan"
