# retoor <retoor@molodetz.nl>

import base64
import binascii
import hashlib
from datetime import datetime, timezone
from fastapi import Request
from devplacepy.database import get_table, is_account_active
from devplacepy.utils.authcache import _user_cache, _sync_auth_cache
from devplacepy.utils.passwords import verify_password

_UNSET = object()


def _user_from_session(request: Request):
    token = request.cookies.get("session")
    if not token:
        return None
    _sync_auth_cache()
    cached = _user_cache.get(token)
    if cached is not None:
        return cached
    sessions = get_table("sessions")
    session = sessions.find_one(session_token=token, deleted_at=None)
    if not session:
        return None
    expires = datetime.fromisoformat(session["expires_at"])
    if expires.tzinfo is None:
        expires = expires.replace(tzinfo=timezone.utc)
    if expires < datetime.now(timezone.utc):
        sessions.delete(id=session["id"])
        return None
    users = get_table("users")
    user = users.find_one(uid=session["user_uid"])
    if user and not is_account_active(user):
        sessions.delete(id=session["id"])
        _user_cache.pop(token)
        return None
    if user:
        _user_cache.set(token, user)
    return user


def _user_from_api_key(key: str):
    if not key:
        return None
    _sync_auth_cache()
    cache_key = f"k:{key}"
    cached = _user_cache.get(cache_key)
    if cached is not None:
        return cached
    user = get_table("users").find_one(api_key=key)
    if not user or not is_account_active(user):
        return None
    _user_cache.set(cache_key, user)
    return user


def _user_from_basic(header: str):
    try:
        raw = base64.b64decode(header.strip(), validate=True).decode("utf-8")
    except (ValueError, binascii.Error, UnicodeDecodeError):
        return None
    if ":" not in raw:
        return None
    identifier, password = raw.split(":", 1)
    identifier = identifier.strip()
    if not identifier or not password:
        return None
    _sync_auth_cache()
    cache_key = "b:" + hashlib.sha256(header.strip().encode()).hexdigest()
    cached = _user_cache.get(cache_key)
    if cached is not None:
        return cached
    users = get_table("users")
    user = users.find_one(username=identifier) or users.find_one(
        email=identifier.lower()
    )
    if not user or not is_account_active(user):
        return None
    if not verify_password(password, user.get("password_hash", "")):
        return None
    _user_cache.set(cache_key, user)
    return user


def _request_has_auth(request: Request) -> bool:
    return bool(
        request.headers.get("X-API-KEY") or request.headers.get("Authorization")
    )


def _resolve_user(request: Request):
    user = _user_from_session(request)
    if user:
        return user

    api_key = request.headers.get("X-API-KEY")
    if api_key:
        api_key = api_key.strip()
        user = _user_from_api_key(api_key)
        if user:
            return user
        user = _user_from_devrant_token(api_key)
        if user:
            return user
        user = _user_from_access_token(api_key)
        if user:
            return user

    authorization = request.headers.get("Authorization", "")
    scheme, _, credentials = authorization.partition(" ")
    scheme = scheme.lower()
    credentials = credentials.strip()
    if scheme == "bearer" and credentials:
        user = _user_from_api_key(credentials)
        if user:
            return user
        user = _user_from_devrant_token(credentials)
        if user:
            return user
        user = _user_from_access_token(credentials)
        if user:
            return user
    if scheme == "basic" and credentials:
        user = _user_from_basic(credentials)
        if user:
            return user
    return None


def _user_from_devrant_token(key: str):
    if not key or len(key) != 40 or not all(c in "0123456789abcdef" for c in key.lower()):
        return None
    _sync_auth_cache()
    cache_key = f"d:{key}"
    cached = _user_cache.get(cache_key)
    if cached is not None:
        return cached
    from devplacepy.services.devrant.tokens import resolve_user_by_key

    user = resolve_user_by_key(key)
    if user:
        _user_cache.set(cache_key, user)
    return user


def _user_from_access_token(key: str):
    if not key or len(key) != 64:
        return None
    _sync_auth_cache()
    cache_key = f"t:{key}"
    cached = _user_cache.get(cache_key)
    if cached is not None:
        return cached
    from devplacepy.services.access_tokens import resolve_token

    user = resolve_token(key)
    if user:
        _user_cache.set(cache_key, user)
    return user


def get_current_user(request: Request):
    cached = getattr(request.state, "_auth_user", _UNSET)
    if cached is not _UNSET:
        return cached
    user = _resolve_user(request)
    try:
        request.state._auth_user = user
    except Exception:
        pass
    return user
