import uuid

from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session

from app.core.security import decode_access_token, verify_api_key
from app.db.session import get_db
from app.models.org import Project, User, UserRole
from app.models.stubs import APIKey

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/v1/auth/login", auto_error=False)

ROLE_RANK = {
    UserRole.viewer: 0,
    UserRole.editor: 1,
    UserRole.admin: 2,
    UserRole.owner: 3,
}


def get_current_user(token: str | None = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> User:
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    if not token:
        raise credentials_exception

    payload = decode_access_token(token)
    if payload is None or "sub" not in payload:
        raise credentials_exception

    user = db.get(User, uuid.UUID(payload["sub"]))
    if user is None or not user.is_active:
        raise credentials_exception
    return user


def get_current_principal(
    token: str | None = Depends(oauth2_scheme),
    db: Session = Depends(get_db),
) -> User:
    """Like get_current_user, but also accepts an API key via the same Authorization
    header (Bearer jz_live_...) for programmatic/external access (Module 16)."""
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    if not token:
        raise credentials_exception

    if token.startswith("jz_live_"):
        prefix = token[:16]
        candidates = db.query(APIKey).filter(APIKey.key_prefix == prefix, APIKey.revoked_at.is_(None)).all()
        for key in candidates:
            if verify_api_key(token, key.hashed_key):
                from datetime import datetime, timezone

                key.last_used_at = datetime.now(timezone.utc)
                db.commit()
                user = db.query(User).filter(User.org_id == key.org_id, User.is_active.is_(True)).first()
                if user is None:
                    raise credentials_exception
                return user
        raise credentials_exception

    return get_current_user(token, db)


def require_role(minimum: UserRole):
    def dependency(current_user: User = Depends(get_current_principal)) -> User:
        if ROLE_RANK[current_user.role] < ROLE_RANK[minimum]:
            raise HTTPException(status_code=403, detail=f"Requires {minimum.value} role or higher")
        return current_user

    return dependency


def get_project_or_404(project_id: uuid.UUID, db: Session, current_user: User) -> Project:
    project = db.get(Project, project_id)
    if project is None or project.org_id != current_user.org_id:
        raise HTTPException(status_code=404, detail="Project not found")
    return project
