from fastapi import Depends, HTTPException, status
from jose import JWTError, jwt
from sqlalchemy import select
from sqlmodel.ext.asyncio.session import AsyncSession

from lexy.core.config import settings
from lexy.core.security import ALGORITHM, oauth2_scheme, verify_password
from lexy.db.session import get_session as get_db, get_sync_engine
from lexy.models.user import User
from lexy.schemas.token import TokenData
from lexy.indexes import IndexManager


async def get_user_by_email(db: AsyncSession, email: str) -> User:
    result = await db.execute(select(User).where(User.email == email))
    user = result.scalars().first()
    return user


async def get_current_user(
    db: AsyncSession = Depends(get_db), token: str = Depends(oauth2_scheme)
) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(
            token, settings.SECRET_KEY.get_secret_value(), algorithms=[ALGORITHM]
        )
        email: str = payload.get("sub")
        if email is None:
            raise credentials_exception
        token_data = TokenData(email=email)
    except JWTError:
        raise credentials_exception
    user = await get_user_by_email(db=db, email=token_data.email)
    if user is None:
        raise credentials_exception
    return user


async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if not current_user.is_active:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


async def get_current_active_superuser(
    current_user: User = Depends(get_current_active_user),
):
    if not current_user.is_superuser:
        raise HTTPException(
            status_code=403, detail="User does not have enough privileges"
        )
    return current_user


async def authenticate_user(db: AsyncSession, *, email: str, password: str):
    user = await get_user_by_email(db=db, email=email)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user


_index_manager: IndexManager | None = None


def get_index_manager() -> IndexManager:
    global _index_manager
    if _index_manager is None:
        engine = get_sync_engine()
        _index_manager = IndexManager(engine=engine)
    return _index_manager


index_manager = get_index_manager()
