"""Fixtures for the vendored conformance pack (`tests/kit/test_*.py`, never edited here).

The pack's contract, kept stable so the five files run verbatim:

  fixtures
    client    a TestClient over https with the Origin header every write must carry.
    bw_calls  list[(path, payload)] of every call the kit made on the directory, recorded
              BEFORE the call runs (so a call that raises is still visible), under the kit's
              central path names: userinfo -> /app/userinfo; invite_user -> /app/invite-user
              (delivery email); create_user_with_password -> the same path (delivery password);
              notify_added -> /app/notify-added; send_reset -> /app/send-reset;
              reset_password_generated -> /app/reset-password; search_users -> /app/search-users;
              report_impersonation -> /app/report-impersonation {real, target, mode, active}.
    kit       a namespace to arrange levels and members as the OWNER, bypassing the API.

The autouse store reset recreates the auth tables on the in-memory SQLite, re-inits the kit
with the owner, seeds exactly the kit's two template levels (`admin`, `member`: the
capabilities test pins the owner's assignable list to those two), and inserts active
principals for the owner and the kit's stubbed central users (alice, aline, bob, carol, dup,
existing, plus dave, whom one test adds as a member directly), because the byte-identical
tests expect `userinfo` to resolve them and `search_users('al')` to return exactly alice and
aline. The mailer under test is a recorder injected into the seam, never a MAIL_PROVIDER.
"""

from __future__ import annotations

from types import SimpleNamespace

import pytest
from fastapi.testclient import TestClient

from app.config import settings
from app.main import app
from app.models import Account
from app.services import accounts, mail
from app.vendor import bw_accounts as bwa
from tests import _accounts
from tests.conftest import OWNER
from tests.kit import _env

CENTRAL_USERS = ("alice", "aline", "bob", "carol", "dup", "existing", "dave")


@pytest.fixture(autouse=True)
def _fresh_store(monkeypatch):
    _env.install()
    _env.reset()
    monkeypatch.setattr(settings, "account_owner", OWNER)
    monkeypatch.setattr(settings, "public_base_url", _accounts.BASE)
    monkeypatch.setattr(settings, "site_access", "members")
    for throttle in _accounts.THROTTLES:
        throttle._keys.clear()
        throttle._all.clear()
    accounts.init()
    bwa.adopt_template(OWNER, "admin")
    bwa.adopt_template(OWNER, "member")
    with _env.TestSessionLocal() as db:
        db.add(Account(username=OWNER, email=f"{OWNER}@example.com", display_name=OWNER.title(), status="active"))
        for name in CENTRAL_USERS:
            db.add(Account(username=name, email=f"{name}@example.com", display_name=name.title(), status="active"))
        db.commit()
    yield


class _Recorder:
    """Wraps the LocalDirectory the kit talks to: every method appends the kit's central
    path name and its arguments to `calls` before running the real method."""

    PATHS = {
        "userinfo": "/app/userinfo", "invite_user": "/app/invite-user",
        "create_user_with_password": "/app/invite-user", "notify_added": "/app/notify-added",
        "send_reset": "/app/send-reset", "reset_password_generated": "/app/reset-password",
        "search_users": "/app/search-users", "report_impersonation": "/app/report-impersonation",
    }

    def __init__(self, inner, calls):
        self._inner, self._calls = inner, calls

    def __getattr__(self, name):
        target = getattr(self._inner, name)
        path = self.PATHS.get(name)
        if path is None or not callable(target):
            return target

        def wrapped(*args, **kwargs):
            payload = self._payload(name, args, kwargs)
            self._calls.append((path, payload))
            return target(*args, **kwargs)

        return wrapped

    @staticmethod
    def _payload(name, args, kwargs):
        if name == "userinfo":
            return {"username": args[0] if args else kwargs.get("username", "")}
        if name in ("invite_user", "create_user_with_password"):
            keys = ("username", "email", "first", "last")
            out = {k: (args[i] if i < len(args) else kwargs.get(k, "")) for i, k in enumerate(keys)}
            out["delivery"] = "password" if name == "create_user_with_password" else "email"
            return out
        if name in ("notify_added", "send_reset", "reset_password_generated"):
            return {"username": args[0] if args else kwargs.get("username", "")}
        if name == "search_users":
            return {"q": args[0] if args else kwargs.get("q", ""), "limit": kwargs.get("limit", args[1] if len(args) > 1 else 8)}
        if name == "report_impersonation":
            real = args[0] if args else kwargs.get("real")
            target = args[1] if len(args) > 1 else kwargs.get("target")
            mode = args[2] if len(args) > 2 else kwargs.get("mode", "readonly")
            active = args[3] if len(args) > 3 else kwargs.get("active", True)
            return {"real": real, "target": target, "mode": mode, "active": active}
        return dict(kwargs)


@pytest.fixture(autouse=True)
def bw_calls(monkeypatch):
    calls: list[tuple[str, dict]] = []
    from app.services.directory import LocalDirectory

    monkeypatch.setattr(accounts, "DIRECTORY", _Recorder(LocalDirectory(), calls))
    monkeypatch.setattr(mail, "_override", _accounts.RecordingMailer())
    return calls


@pytest.fixture
def client():
    with TestClient(app, raise_server_exceptions=False, base_url=_accounts.BASE,
                    headers={"Origin": _accounts.BASE}) as test_client:
        yield test_client


@pytest.fixture
def kit():
    def member(username, level, all_instances=False):
        bwa.add_member(OWNER, username, level, all_instances=all_instances)

    def instance(iid, label=None):
        bwa.create_instance(OWNER, iid, label or iid)

    def grant(username, iid, level):
        bwa.grant_instance(OWNER, username, iid, level)

    def set_level(username, level):
        bwa.set_member_level(OWNER, username, level)

    def set_all(username, value):
        bwa.set_all_instances(OWNER, username, value)

    return SimpleNamespace(bwa=bwa, owner=OWNER, member=member, instance=instance, grant=grant,
                           set_level=set_level, set_all=set_all)
