# retoor <retoor@molodetz.nl>

import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional

from devplacepy.database import get_table, get_int_setting, is_account_active
from devplacepy.config import SECONDS_PER_DAY
from devplacepy.utils import generate_uid

TOKEN_KEY_BYTES = 32  # 64-char hex, matching session token size


def issue_token(
    user: dict,
    label: str = "",
    max_age_days: Optional[int] = None,
) -> dict:
    if max_age_days is None:
        max_age_days = max(1, get_int_setting("session_max_age_days", 7))
    max_age_seconds = max_age_days * SECONDS_PER_DAY

    token = secrets.token_hex(TOKEN_KEY_BYTES)
    now = datetime.now(timezone.utc)
    expires_at = now + timedelta(seconds=max_age_seconds)

    token_uid = generate_uid()
    tokens = get_table("access_tokens")
    tokens.insert(
        {
            "uid": token_uid,
            "token": token,
            "user_uid": user["uid"],
            "label": label or "",
            "expires_at": expires_at.isoformat(),
            "created_at": now.isoformat(),
            "deleted_at": None,
            "deleted_by": None,
        }
    )

    return {
        "access_token": token,
        "token_type": "bearer",
        "expires_in": max_age_seconds,
        "expires_at": expires_at.isoformat(),
        "uid": token_uid,
    }


def resolve_token(token: str) -> Optional[dict]:
    if not token:
        return None
    row = get_table("access_tokens").find_one(token=token, deleted_at=None)
    if not row:
        return None

    expires_at = row.get("expires_at", "")
    if expires_at:
        try:
            expires = datetime.fromisoformat(expires_at)
            if expires.tzinfo is None:
                expires = expires.replace(tzinfo=timezone.utc)
        except (ValueError, TypeError):
            return None
        if expires < datetime.now(timezone.utc):
            return None

    user = get_table("users").find_one(uid=row.get("user_uid"))
    if not user or not is_account_active(user):
        return None
    return user


def revoke_token(uid: str) -> bool:
    tokens = get_table("access_tokens")
    row = tokens.find_one(uid=uid, deleted_at=None)
    if not row:
        return False
    stamp = datetime.now(timezone.utc).isoformat()
    tokens.update({"id": row["id"], "deleted_at": stamp, "deleted_by": "manual"}, ["id"])
    from devplacepy.utils.authcache import clear_user_cache

    clear_user_cache(row["user_uid"])
    return True


def revoke_all(user_uid: str) -> int:
    tokens = get_table("access_tokens")
    stamp = datetime.now(timezone.utc).isoformat()
    count = 0
    for row in list(tokens.find(user_uid=user_uid, deleted_at=None)):
        tokens.update({"id": row["id"], "deleted_at": stamp, "deleted_by": "manual"}, ["id"])
        count += 1
    if count:
        from devplacepy.utils.authcache import clear_user_cache

        clear_user_cache(user_uid)
    return count


def prune_expired() -> int:
    tokens = get_table("access_tokens")
    now = datetime.now(timezone.utc)
    stamp = now.isoformat()
    count = 0
    for row in list(tokens.find(deleted_at=None)):
        expires_at = row.get("expires_at", "")
        if not expires_at:
            continue
        try:
            expires = datetime.fromisoformat(expires_at)
            if expires.tzinfo is None:
                expires = expires.replace(tzinfo=timezone.utc)
        except (ValueError, TypeError):
            continue
        if expires < now:
            tokens.update({"id": row["id"], "deleted_at": stamp, "deleted_by": "prune"}, ["id"])
            count += 1
    return count
