"""POST /api/me/onboarded — flips users.onboarded TRUE for the calling user.

Self-scoped, idempotent, audit-logged. Body optional pydantic model with
`source` field distinguishing 'agnes_init' (CLI auto-fire) from
'self_acknowledged' (the on-page button for users who already set up
locally before v28 shipped).

See origin: docs/brainstorms/home-page-requirements.md §2 + §6.
"""

from __future__ import annotations

import json
import tempfile
import uuid

import pytest


@pytest.fixture
def fresh_db(monkeypatch):
    """Per-test DATA_DIR + JWT secret so the system DB is fresh."""
    with tempfile.TemporaryDirectory() as tmp:
        monkeypatch.setenv("DATA_DIR", tmp)
        monkeypatch.setenv("TESTING", "1")
        monkeypatch.setenv("JWT_SECRET_KEY", "test-jwt-secret-key-minimum-32-chars!!")
        yield tmp


def _make_user_and_session(conn, email: str = "u@example.com"):
    """Create a non-admin user, return (user_id, session_jwt)."""
    from src.repositories.users import UserRepository
    from app.auth.jwt import create_access_token

    uid = str(uuid.uuid4())
    UserRepository(conn).create(id=uid, email=email, name=email.split("@")[0])
    token = create_access_token(user_id=uid, email=email)
    return uid, token


def _client():
    from fastapi.testclient import TestClient
    from app.main import app

    return TestClient(app)


def _onboarded(conn, user_id: str) -> bool:
    row = conn.execute(
        "SELECT onboarded FROM users WHERE id = ?", [user_id]
    ).fetchone()
    return bool(row[0])


def _audit_rows(conn, user_id: str, action: str = "user_onboarded"):
    return conn.execute(
        "SELECT id, action, params FROM audit_log "
        "WHERE user_id = ? AND action = ? ORDER BY timestamp",
        [user_id, action],
    ).fetchall()


def test_post_flips_onboarded_to_true(fresh_db):
    """Authed user with onboarded=FALSE → POST → onboarded=TRUE."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        uid, sess = _make_user_and_session(conn)
        assert _onboarded(conn, uid) is False
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp = c.post("/api/me/onboarded", cookies={"access_token": sess})
    assert resp.status_code == 200
    body = resp.json()
    assert body["onboarded"] is True

    conn = get_system_db()
    try:
        assert _onboarded(conn, uid) is True
    finally:
        conn.close()
        close_system_db()


def test_post_default_source_is_agnes_init(fresh_db):
    """Empty body defaults to source='agnes_init' in audit_log."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        uid, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp = c.post("/api/me/onboarded", cookies={"access_token": sess})
    assert resp.status_code == 200

    conn = get_system_db()
    try:
        rows = _audit_rows(conn, uid)
        assert len(rows) == 1
        params = json.loads(rows[0][2]) if isinstance(rows[0][2], str) else rows[0][2]
        assert params.get("source") == "agnes_init"
    finally:
        conn.close()
        close_system_db()


def test_post_self_acknowledged_source(fresh_db):
    """Body {source: 'self_acknowledged'} from /home self-mark button."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        uid, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp = c.post(
        "/api/me/onboarded",
        json={"source": "self_acknowledged"},
        cookies={"access_token": sess},
    )
    assert resp.status_code == 200

    conn = get_system_db()
    try:
        rows = _audit_rows(conn, uid)
        params = json.loads(rows[0][2]) if isinstance(rows[0][2], str) else rows[0][2]
        assert params.get("source") == "self_acknowledged"
    finally:
        conn.close()
        close_system_db()


def test_post_idempotent_already_onboarded(fresh_db):
    """Second POST on already-onboarded user returns 200 + writes second
    audit_log row (cheap visibility on duplicate calls)."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        uid, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp1 = c.post("/api/me/onboarded", cookies={"access_token": sess})
    assert resp1.status_code == 200
    resp2 = c.post("/api/me/onboarded", cookies={"access_token": sess})
    assert resp2.status_code == 200

    conn = get_system_db()
    try:
        rows = _audit_rows(conn, uid)
        assert len(rows) == 2  # both calls logged
        assert _onboarded(conn, uid) is True
    finally:
        conn.close()
        close_system_db()


def test_post_unauthenticated_returns_401(fresh_db):
    c = _client()
    resp = c.post("/api/me/onboarded")
    assert resp.status_code in (401, 403)  # depending on auth dependency shape


def test_post_invalid_source_returns_422(fresh_db):
    """Pydantic rejects unknown source value."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        _, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp = c.post(
        "/api/me/onboarded",
        json={"source": "fabricated"},
        cookies={"access_token": sess},
    )
    assert resp.status_code == 422


def test_post_offboard_flips_to_false(fresh_db):
    """Body {onboarded: false, source: 'self_unmark'} flips back. Used by the
    'Mark me as offboarded' button shown on the post-onboarding /home view."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        uid, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp1 = c.post("/api/me/onboarded", cookies={"access_token": sess})
    assert resp1.status_code == 200
    resp2 = c.post(
        "/api/me/onboarded",
        json={"source": "self_unmark", "onboarded": False},
        cookies={"access_token": sess},
    )
    assert resp2.status_code == 200
    assert resp2.json()["onboarded"] is False

    conn = get_system_db()
    try:
        assert _onboarded(conn, uid) is False
        on_rows = _audit_rows(conn, uid, action="user_onboarded")
        off_rows = _audit_rows(conn, uid, action="user_offboarded")
        assert len(on_rows) == 1, "first POST should write user_onboarded"
        assert len(off_rows) == 1, "second POST should write user_offboarded"
        off_params = json.loads(off_rows[0][2]) if isinstance(off_rows[0][2], str) else off_rows[0][2]
        assert off_params.get("source") == "self_unmark"
    finally:
        conn.close()
        close_system_db()


def test_post_invalid_source_self_unmark_accepted(fresh_db):
    """`self_unmark` is now a valid source value (used by the offboard button)."""
    from src.db import get_system_db, close_system_db

    conn = get_system_db()
    try:
        _, sess = _make_user_and_session(conn)
    finally:
        conn.close()
        close_system_db()

    c = _client()
    resp = c.post(
        "/api/me/onboarded",
        json={"source": "self_unmark", "onboarded": False},
        cookies={"access_token": sess},
    )
    assert resp.status_code == 200
