"""Editing a comment: the body changes, the author never does, and it says so.

Since the app's own login the author is the session's account: one's own comment is
editable under client.participate, anyone's under discussion.curate (the owner holds both
by absence). The route's database is a fake; the session store is the in-memory one.
"""

from datetime import UTC, datetime

import pytest
from pydantic import ValidationError

from app.models.schemas import FeatureCommentEdit

# The timestamps are server defaults, so a row built in memory has none; the
# response model needs them, so the stubs carry them explicitly.
POSTED = datetime(2026, 9, 7, 12, 0, tzinfo=UTC)


class TestEditSchema:
    def test_body_is_stripped(self):
        assert FeatureCommentEdit(body="  fixed a typo  ").body == "fixed a typo"

    def test_empty_body_is_refused(self):
        # A comment is never emptied by editing; deleting is a different act.
        with pytest.raises(ValidationError):
            FeatureCommentEdit(body="   ")

    def test_over_long_body_is_refused(self):
        with pytest.raises(ValidationError):
            FeatureCommentEdit(body="x" * 2001)

    def test_author_is_not_an_editable_field(self):
        # Re-attributing someone's words is not a correction; the schema has no
        # author, and pydantic drops anything extra rather than applying it.
        assert not hasattr(FeatureCommentEdit(body="ok"), "author")


class FakeSession:
    """Just enough session for the edit route: fetch, write, no network or DB."""

    def __init__(self, row=None):
        self.row, self.committed = row, False

    def get(self, _model, _pk):
        return self.row

    def commit(self):
        self.committed = True

    def refresh(self, _row):
        pass


@pytest.fixture
def client(monkeypatch):
    from app.db import get_db
    from app.main import app
    from tests import _accounts as T

    # The route sits behind client.participate since the app's own login: an owner session
    # on the in-memory accounts store; the route's own database stays the fake below.
    T.fresh(monkeypatch)
    T.person("rian")
    sessions = {}

    def make(row):
        sessions["s"] = FakeSession(row)
        app.dependency_overrides[get_db] = lambda: sessions["s"]
        c = T.client()
        T.as_user(c, "rian")
        return c, sessions["s"]

    yield make
    app.dependency_overrides.clear()


class TestEditRoute:
    def _comment(self):
        from app.models import DiscussionComment

        return DiscussionComment(
            id=7, feature_key="structure:urls", author="Mark", author_id=99, body="the original words",
            created_at=POSTED, updated_at=POSTED,
        )

    def test_body_is_rewritten_and_author_is_untouched(self, client):
        c, session = client(self._comment())
        r = c.post("/api/discussion/feature-comments/7", json={"body": "the corrected words"})
        assert r.status_code == 200
        assert session.row.body == "the corrected words"
        assert session.row.author == "Mark"
        assert session.committed

    def test_an_author_sent_in_the_payload_is_ignored(self, client):
        c, session = client(self._comment())
        r = c.post(
            "/api/discussion/feature-comments/7",
            json={"body": "still Mark's", "author": "Someone else"},
        )
        assert r.status_code == 200
        assert session.row.author == "Mark"

    def test_unknown_comment_is_a_404(self, client):
        c, session = client(None)
        assert c.post("/api/discussion/feature-comments/999", json={"body": "x"}).status_code == 404
        assert not session.committed

    def test_a_discussion_item_comment_is_not_editable_here(self, client):
        from app.models import DiscussionComment

        # item_id comments belong to the item threads, not the feature threads.
        c, session = client(
            DiscussionComment(id=8, item_id=3, author="a", body="b",
                              created_at=POSTED, updated_at=POSTED)
        )
        assert c.post("/api/discussion/feature-comments/8", json={"body": "x"}).status_code == 404
        assert not session.committed

    def test_empty_body_is_refused_by_the_route(self, client):
        c, session = client(self._comment())
        assert c.post("/api/discussion/feature-comments/7", json={"body": "  "}).status_code == 422
        assert session.row.body == "the original words"
