from datetime import datetime, timedelta, timezone
from typing import Optional, List
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status, Request, Header
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session
from .config import settings
from .database import get_db
from .models.user import User
from .models.permission import Permission, RolePermission
from .i18n import get_supported_languages, DEFAULT_LANGUAGE

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


def get_token_from_cookie(request: Request) -> Optional[str]:
    """Get access token from HttpOnly cookie"""
    return request.cookies.get("access_token")


async def get_language(
    accept_language: Optional[str] = Header(None),
    lang: Optional[str] = None
) -> str:
    """
    Get the preferred language from Accept-Language header or query parameter.
    
    Args:
        accept_language: Accept-Language header (e.g., "de-DE,de;q=0.9,en;q=0.8")
        lang: Language query parameter (e.g., "de")
    
    Returns:
        The language code (e.g., "de", "en")
    """
    # Priority: Query parameter > Accept-Language header > Default
    if lang and lang in get_supported_languages():
        return lang
    
    if accept_language:
        # Parse Accept-Language header
        # Format: "de-DE,de;q=0.9,en;q=0.8"
        languages = accept_language.split(',')
        for lang_pair in languages:
            # Split language and quality (q)
            lang_part = lang_pair.split(';')[0].strip()
            # Extract language code (e.g., "de" from "de-DE")
            lang_code = lang_part.split('-')[0].lower()
            if lang_code in get_supported_languages():
                return lang_code
    
    return DEFAULT_LANGUAGE


def verify_password(plain_password: str, hashed_password: str) -> bool:
    return pwd_context.verify(plain_password, hashed_password)


def get_password_hash(password: str) -> str:
    return pwd_context.hash(password)


def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
    return encoded_jwt


def create_refresh_token(data: dict) -> str:
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + timedelta(days=settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS)
    to_encode.update({"exp": expire, "type": "refresh"})
    encoded_jwt = jwt.encode(to_encode, settings.JWT_SECRET_KEY, algorithm=settings.JWT_ALGORITHM)
    return encoded_jwt


async def get_current_user(
    request: Request,
    db: Session = Depends(get_db)
) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    
    # Try to get token from cookie first
    token = get_token_from_cookie(request)
    
    # Fallback to Authorization header
    if not token:
        token = await oauth2_scheme(request)
    
    if not token:
        raise credentials_exception
    
    try:
        payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            raise credentials_exception
    except JWTError:
        raise credentials_exception
    
    user = db.query(User).filter(User.id == user_id).first()
    if user is None:
        raise credentials_exception
    return user


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


async def get_current_admin_user(
    current_user: User = Depends(get_current_active_user)
) -> User:
    if not current_user.role_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="No role assigned"
        )
    if current_user.role.name != "admin":
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Admin access required"
        )
    return current_user


async def get_current_mod_user(
    current_user: User = Depends(get_current_active_user)
) -> User:
    if not current_user.role_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="No role assigned"
        )
    if current_user.role.name not in ["mod", "admin"]:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Moderator access required"
        )
    return current_user


async def get_current_creator_user(
    current_user: User = Depends(get_current_active_user)
) -> User:
    if not current_user.role_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="No role assigned"
        )
    if current_user.role.name not in ["creator", "admin"]:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Creator access required"
        )
    return current_user


async def get_current_subscriber_user(
    current_user: User = Depends(get_current_active_user)
) -> User:
    if not current_user.role_id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="No role assigned"
        )
    if current_user.role.name not in ["subscriber", "creator", "admin"]:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Subscriber access required"
        )
    return current_user


async def get_current_user_optional(
    request: Request,
    db: Session = Depends(get_db)
) -> Optional[User]:
    """Get current user from token (cookie or header), return None if not authenticated"""
    # Try to get token from cookie first
    token = get_token_from_cookie(request)
    
    # Fallback to Authorization header
    if not token:
        token = await oauth2_scheme(request)
    
    if not token:
        return None
    
    try:
        payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms=[settings.JWT_ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            return None
        user = db.query(User).filter(User.id == user_id).first()
        return user
    except (JWTError, Exception):
        return None


def get_user_permissions(user: User, db: Session) -> List[str]:
    """Get all permission names for a user based on their role"""
    if not user.role_id:
        return []
    
    # Get all permissions for the user's role
    permissions = db.query(Permission).join(RolePermission).filter(
        RolePermission.role_id == user.role_id
    ).all()
    
    return [perm.name for perm in permissions]


def has_permission(user: User, permission_name: str, db: Session) -> bool:
    """Check if user has a specific permission"""
    if not user.role_id:
        return False
    
    # Admin users have all permissions
    if user.role.name == "admin":
        return True
    
    # Check if user has the specific permission
    permission = db.query(Permission).join(RolePermission).filter(
        RolePermission.role_id == user.role_id,
        Permission.name == permission_name
    ).first()
    
    return permission is not None


def require_permission(permission_name: str):
    """Dependency factory to require a specific permission"""
    async def permission_dependency(
        current_user: User = Depends(get_current_active_user),
        db: Session = Depends(get_db)
    ) -> User:
        if not has_permission(current_user, permission_name, db):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"Permission required: {permission_name}"
            )
        return current_user
    return permission_dependency


def require_any_permission(permission_names: List[str]):
    """Dependency factory to require any of the specified permissions"""
    async def permission_dependency(
        current_user: User = Depends(get_current_active_user),
        db: Session = Depends(get_db)
    ) -> User:
        # Admin users have all permissions
        if current_user.role and current_user.role.name == "admin":
            return current_user
        
        # Check if user has any of the required permissions
        for perm_name in permission_names:
            if has_permission(current_user, perm_name, db):
                return current_user
        
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=f"One of these permissions required: {', '.join(permission_names)}"
        )
    return permission_dependency
