# retoor <retoor@molodetz.nl>

import secrets
from datetime import datetime, timezone

from devplacepy.database import get_table
from devplacepy.utils import generate_uid, _user_from_devrant_token, _resolve_user
from devplacepy.services.devrant.tokens import resolve_user_by_key


# ── mock helpers ─────────────────────────────────────────────────────────────

class _MockHeaders:
    def __init__(self, data: dict):
        self._data = data

    def get(self, key: str, default=None):
        return self._data.get(key, default)


class _MockCookies:
    def __init__(self, data: dict):
        self._data = data

    def get(self, key: str):
        return self._data.get(key)


class _MockRequest:
    def __init__(self, headers=None, cookies=None):
        self.headers = _MockHeaders(headers or {})
        self.cookies = _MockCookies(cookies or {})


# ── seed helpers ─────────────────────────────────────────────────────────────

def _seed_user(username, email, is_active=True, **extra):
    uid = generate_uid()
    get_table("users").insert(
        {
            "uid": uid,
            "username": username,
            "terms_version": "1",
            "email": email,
            "is_active": is_active,
            "created_at": datetime.now(timezone.utc).isoformat(),
            **extra,
        }
    )
    return uid


def _seed_token(user_uid, key, expire_time):
    get_table("devrant_tokens").insert(
        {
            "uid": generate_uid(),
            "key": key,
            "user_uid": user_uid,
            "user_id": 1,
            "expire_time": expire_time,
            "created_at": datetime.now(timezone.utc).isoformat(),
            "deleted_at": None,
            "deleted_by": None,
        }
    )


# ── resolve_user_by_key tests ────────────────────────────────────────────────

def test_resolve_user_by_key_valid(local_db):
    """Create a devrant token for a user, then resolve it by key - the right user is returned."""
    uid = _seed_user("dr_valid", "drvalid@test.dev")
    token_key = secrets.token_hex(20)
    expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
    _seed_token(uid, token_key, expire_time)

    user = resolve_user_by_key(token_key)
    assert user is not None
    assert user["uid"] == uid
    assert user["username"] == "dr_valid"


def test_resolve_user_by_key_not_found(local_db):
    """Look up a random key that has no token - verify None."""
    random_key = secrets.token_hex(20)
    assert resolve_user_by_key(random_key) is None


def test_resolve_user_by_key_expired(local_db):
    """Create an expired token - resolve returns None."""
    uid = _seed_user("dr_expired", "drexpired@test.dev")
    token_key = secrets.token_hex(20)
    expire_time = int(datetime.now(timezone.utc).timestamp()) - 86400
    _seed_token(uid, token_key, expire_time)

    assert resolve_user_by_key(token_key) is None


def test_resolve_user_by_key_inactive_user(local_db):
    """Create a token for an inactive user - resolve returns None."""
    uid = _seed_user("dr_inactive", "drinactive@test.dev", is_active=False)
    token_key = secrets.token_hex(20)
    expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
    _seed_token(uid, token_key, expire_time)

    assert resolve_user_by_key(token_key) is None


# ── _user_from_devrant_token hex-format guard tests ──────────────────────────

def test_user_from_devrant_token_hex_format(local_db):
    """_user_from_devrant_token only tries DB for 40-char hex strings;
    non-hex or wrong-length keys return None without a DB hit."""
    # empty / falsy
    assert _user_from_devrant_token("") is None
    assert _user_from_devrant_token(None) is None

    # wrong length
    assert _user_from_devrant_token("abc123") is None
    assert _user_from_devrant_token("a" * 39) is None
    assert _user_from_devrant_token("a" * 41) is None

    # right length but non-hex
    assert _user_from_devrant_token("z" * 40) is None
    assert _user_from_devrant_token(("g" * 39) + "x") is None

    # valid 40-char hex that simply has no matching token in the DB
    assert _user_from_devrant_token("a" * 40) is None


# ── _resolve_user bridge tests ───────────────────────────────────────────────

def test_resolve_user_falls_back_to_devrant_token(local_db):
    """_resolve_user finds a user from X-API-KEY header containing a DevRant token key."""
    uid = _seed_user("dr_fallback", "drfallback@test.dev")
    token_key = secrets.token_hex(20)
    expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
    _seed_token(uid, token_key, expire_time)

    request = _MockRequest(headers={"X-API-KEY": token_key})
    user = _resolve_user(request)
    assert user is not None
    assert user["uid"] == uid
    assert user["username"] == "dr_fallback"


def test_resolve_user_falls_back_to_devrant_token_bearer(local_db):
    """_resolve_user finds a user from Authorization: Bearer header containing a DevRant token key."""
    uid = _seed_user("dr_bearer", "drbearer@test.dev")
    token_key = secrets.token_hex(20)
    expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
    _seed_token(uid, token_key, expire_time)

    request = _MockRequest(headers={"Authorization": f"Bearer {token_key}"})
    user = _resolve_user(request)
    assert user is not None
    assert user["uid"] == uid
    assert user["username"] == "dr_bearer"


def test_resolve_user_prefers_api_key_over_devrant(local_db):
    """If a key matches both an api_key and a devrant token, the api_key user wins."""
    shared_key = secrets.token_hex(20)

    # User A - owns the api_key
    uid_a = _seed_user("api_key_owner", "apikey@test.dev", api_key=shared_key)

    # User B - owns the devrant token with the same key
    uid_b = _seed_user("devrant_owner", "drowner@test.dev")
    expire_time = int(datetime.now(timezone.utc).timestamp()) + 86400
    _seed_token(uid_b, shared_key, expire_time)

    # ── X-API-KEY path ──
    request = _MockRequest(headers={"X-API-KEY": shared_key})
    user = _resolve_user(request)
    assert user is not None
    assert user["uid"] == uid_a
    assert user["username"] == "api_key_owner"

    # ── Bearer path ──
    request2 = _MockRequest(headers={"Authorization": f"Bearer {shared_key}"})
    user2 = _resolve_user(request2)
    assert user2 is not None
    assert user2["uid"] == uid_a
    assert user2["username"] == "api_key_owner"
