# app/auth.py

from fastapi import Depends, HTTPException
from jose import JWTError, jwt
from datetime import datetime, timedelta
from passlib.context import CryptContext
from fastapi.security import OAuth2PasswordBearer
from app.database import db
from app.models import UserInDB 
from app.config import config
from fastapi import Cookie
from bson import ObjectId 

SECRET_KEY = config.SECRET_KEY or "cidti40"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60

pwd_context = CryptContext(schemes=["pbkdf2_sha256", "bcrypt_sha256", "bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

BCRYPT_MAX_BYTES = 72

def _is_bcrypt_hash(hashed_password: str) -> bool:
    return hashed_password.startswith(("$2a$", "$2b$", "$2y$", "$2x$"))

def verify_password(plain_password, hashed_password):
    try:
        if _is_bcrypt_hash(hashed_password):
            if len(plain_password.encode("utf-8")) > BCRYPT_MAX_BYTES:
                return False
        return pwd_context.verify(plain_password, hashed_password)
    except ValueError:
        return False

def hash_password(password):
    return pwd_context.hash(password)

async def authenticate_user(username: str, password: str):
    user_data = await db.users.find_one({"username": username})
    if not user_data or not verify_password(password, user_data["password"]):
        return False

    user_data["_id"] = str(user_data["_id"])
    return UserInDB(**user_data)

def create_access_token(data: dict):
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)


async def get_current_user(access_token: str = Cookie(default=None)):
    credentials_exception = HTTPException(
        status_code=401,
        detail="Not authenticated",
        headers={"X-Auth-Error": "auth_expired"},
    )

    if not access_token:
        raise credentials_exception

    try:
        token = access_token.replace("Bearer ", "")
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username = payload.get("sub")
        user_data = await db.users.find_one({"username": username})

        if not user_data:
            raise credentials_exception

        user_data["_id"] = str(user_data["_id"])
        return UserInDB(**user_data)

    except JWTError:
        raise credentials_exception

def require_role(role: str):
    async def role_checker(user: UserInDB = Depends(get_current_user)):
        if user.role != role:
            raise HTTPException(status_code=403, detail="Acceso denegado")
        return user
    return role_checker
