from typing import Optional
from datetime import datetime, timedelta
from cachetools import TTLCache
from sqlmodel import Session
from app.crud import user as crud_user

# Configurable parameters
MAX_FAILED_ATTEMPTS = 5
LOCKOUT_TTL_SECONDS = 15 * 60  # 15 minutes
CACHE_MAXSIZE = 10000

# In-memory caches (process-local)
_failed_login_cache = TTLCache(maxsize=CACHE_MAXSIZE, ttl=LOCKOUT_TTL_SECONDS)
_lockout_cache = TTLCache(maxsize=CACHE_MAXSIZE, ttl=LOCKOUT_TTL_SECONDS)


def is_locked(username: str) -> bool:
    """Return True if the username is currently locked out."""
    return username in _lockout_cache


def record_failed_login(session: Session, username: str, user_id: Optional[int] = None) -> dict:
    """Record a failed login attempt for `username`.

    Returns a dict with keys:
      - locked: bool  (True when the account was locked by this call)
      - attempts: int (current attempt count)
    """
    attempts = _failed_login_cache.get(username, 0) + 1
    _failed_login_cache[username] = attempts

    # Log the attempt (best-effort)
    try:
        crud_user.log_activity(session, user_id, "failed_login", f"attempts={attempts}")
    except Exception:
        pass

    if attempts >= MAX_FAILED_ATTEMPTS:
        _lockout_cache[username] = True
        # Persist lockout to DB so it survives worker restarts
        if user_id:
            try:
                from sqlmodel import select
                from app.models.models import User
                u = session.exec(select(User).where(User.id == user_id)).one_or_none()
                if u:
                    u.lockout_until = datetime.utcnow() + timedelta(seconds=LOCKOUT_TTL_SECONDS)
                    session.add(u)
                    session.commit()
            except Exception:
                pass
        try:
            del _failed_login_cache[username]
        except KeyError:
            pass
        return {"locked": True, "attempts": attempts}

    return {"locked": False, "attempts": attempts}


def clear_attempts(username: str) -> None:
    """Clear any tracked failed attempts and lockout for a username."""
    try:
        if username in _failed_login_cache:
            del _failed_login_cache[username]
    except Exception:
        pass
    try:
        if username in _lockout_cache:
            del _lockout_cache[username]
    except Exception:
        pass


def record_successful_login(session: Session, user_id: int, username: str) -> None:
    """Clear failed-attempts and log a successful login."""
    clear_attempts(username)
    # Clear any DB-persisted lockout so it doesn't outlive an in-memory clear
    if user_id:
        try:
            from sqlmodel import select
            from app.models.models import User
            u = session.exec(select(User).where(User.id == user_id)).one_or_none()
            if u and u.lockout_until is not None:
                u.lockout_until = None
                session.add(u)
                session.commit()
        except Exception:
            pass
    try:
        crud_user.log_activity(session, user_id, "login_success", None)
    except Exception:
        pass
