import os
from datetime import datetime, timedelta
from passlib.context import CryptContext
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from sqlalchemy.orm import selectinload

from database import get_db
from models import User, Role, Company

SECRET_KEY = os.getenv("JWT_SECRET_KEY", "super_secret_local_key_for_dev_only")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 # 1 day

pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/auth/login")

def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

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

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
    except jwt.PyJWTError:
        raise credentials_exception
        
    stmt = select(User).options(selectinload(User.roles), selectinload(User.company), selectinload(User.viewable_projects)).where(User.username == username)
    result = await db.execute(stmt)
    user = result.scalar_one_or_none()
    
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
    if not current_user.is_active:
        raise HTTPException(status_code=400, detail="Inactive user")
        
    user_roles = [role.name for role in current_user.roles]
    is_superadmin = "Super Administrador" in user_roles
    
    if not is_superadmin:
        if not current_user.company or not current_user.company.is_active:
            raise HTTPException(status_code=400, detail="La empresa asociada está inactiva o no existe.")
        
    # Actualizar last_active si pasaron más de 1 minuto para no saturar la BD
    now = datetime.utcnow()
    if not current_user.last_active or (now - current_user.last_active).total_seconds() > 60:
        current_user.last_active = now
        db.add(current_user)
        await db.commit()
        # Volver a cargar el usuario con sus roles explícitamente para evitar MissingGreenlet
        from sqlalchemy.orm import selectinload
        stmt = select(User).options(selectinload(User.roles), selectinload(User.company), selectinload(User.viewable_projects)).where(User.id == current_user.id)
        result = await db.execute(stmt)
        current_user = result.scalar_one()
        
    return current_user

# RBAC Dependencies
def require_roles(allowed_roles: list[str]):
    async def role_checker(current_user: User = Depends(get_current_active_user)):
        try:
            user_roles = [role.name for role in current_user.roles]
            if "Super Administrador" in user_roles or "Administrador" in user_roles:
                return current_user # Admin has all permissions
            
            has_role = any(role in user_roles for role in allowed_roles)
            if not has_role:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail="No tienes los permisos necesarios para realizar esta acción."
                )
            return current_user
        except Exception as e:
            import traceback
            traceback.print_exc()
            raise HTTPException(status_code=500, detail=str(e))
    return role_checker

async def require_helpdesk_access(current_user: User = Depends(get_current_active_user)):
    user_roles = [role.name for role in current_user.roles]
    if "Super Administrador" in user_roles or "Administrador" in user_roles:
        return current_user
    if "Miembro" in user_roles or "Visualizador" in user_roles:
        raise HTTPException(
            status_code=403,
            detail="No tienes acceso al módulo de Helpdesk."
        )
    return current_user

async def require_projects_access(current_user: User = Depends(get_current_active_user)):
    user_roles = [role.name for role in current_user.roles]
    if "Super Administrador" in user_roles or "Administrador" in user_roles:
        return current_user
    if "Agente" in user_roles:
        raise HTTPException(
            status_code=403,
            detail="No tienes acceso al módulo de Proyectos."
        )
    return current_user

async def require_metrics_access(current_user: User = Depends(get_current_active_user)):
    user_roles = [role.name for role in current_user.roles]
    if "Super Administrador" in user_roles or "Administrador" in user_roles:
        return current_user
    if "Project Manager" not in user_roles:
        raise HTTPException(
            status_code=403,
            detail="No tienes acceso al módulo de Métricas."
        )
    return current_user
