import logging

from fastapi import APIRouter, Depends, HTTPException, Response, status
from sqlalchemy.orm import Session
from starlette.requests import Request

from app.core.rbac import get_user_permission_names, require_permission
from app.db.database import get_db
from app.models.user import User
from app.repositories.rbac_repository import RbacRepository
from app.repositories.user_repository import UserRepository
from app.schemas.user import UserCreate, UserPasswordUpdate, UserResponse, UserUpdate
from app.services.audit_service import sanitize, write_audit_log

logger = logging.getLogger(__name__)

router = APIRouter(
    prefix="/users",
    tags=["Users"],
)

def get_user_or_404(db: Session, user_id: int) -> User:
    db_user = UserRepository.get_by_id(db, user_id)

    if db_user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Benutzer nicht gefunden",
        )

    return db_user


def raise_conflict(field: str) -> None:
    if field == "username":
        detail = "Benutzername ist bereits vergeben"
    else:
        detail = "E-Mail ist bereits vergeben"

    raise HTTPException(
        status_code=status.HTTP_409_CONFLICT,
        detail=detail,
    )


def require_actor_permission(current_user: User, permission: str) -> None:
    if permission not in get_user_permission_names(current_user):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Keine Berechtigung",
        )


def apply_role(db: Session, user: UserCreate | UserUpdate) -> None:
    role = None
    if user.role_id is not None:
        role = RbacRepository.get_role_by_id(db, user.role_id)
    else:
        role = RbacRepository.get_role_by_name(db, user.role)

    if role is None:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Rolle nicht gefunden",
        )

    user.role_id = role.id
    user.role = role.name


@router.get("", response_model=list[UserResponse])
@router.get("/", response_model=list[UserResponse], include_in_schema=False)
def get_users(
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.read")),
):
    logger.info("users.list", extra={"actor_user_id": current_user.id})
    return UserRepository.get_all(db)


@router.get("/{user_id}", response_model=UserResponse)
def get_user(
    user_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.read")),
):
    logger.info(
        "users.detail",
        extra={"actor_user_id": current_user.id, "target_user_id": user_id},
    )
    return get_user_or_404(db, user_id)


@router.post("", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
@router.post(
    "/",
    response_model=UserResponse,
    status_code=status.HTTP_201_CREATED,
    include_in_schema=False,
)
def create_user(
    user: UserCreate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.create")),
):
    require_actor_permission(current_user, "roles.assign")
    apply_role(db, user)
    conflict = UserRepository.find_conflict(
        db,
        username=user.username,
        email=str(user.email),
    )

    if conflict is not None:
        raise_conflict(conflict[0])

    logger.info("users.create", extra={"actor_user_id": current_user.id})
    created_user = UserRepository.create(db, user)
    write_audit_log(
        db,
        action="users.create",
        entity_type="users",
        entity_id=created_user.id,
        entity_label=created_user.username,
        actor=current_user,
        request=request,
        after_data=created_user,
    )
    return created_user


@router.put("/{user_id}", response_model=UserResponse)
def update_user(
    user_id: int,
    user: UserUpdate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.update")),
):
    db_user = get_user_or_404(db, user_id)
    before_data = sanitize(db_user)
    apply_role(db, user)
    if user.role_id != db_user.role_id:
        require_actor_permission(current_user, "roles.assign")
    conflict = UserRepository.find_conflict(
        db,
        username=user.username,
        email=str(user.email),
        exclude_user_id=user_id,
    )

    if conflict is not None:
        raise_conflict(conflict[0])

    logger.info(
        "users.update",
        extra={"actor_user_id": current_user.id, "target_user_id": user_id},
    )
    updated_user = UserRepository.update(db, db_user, user)
    write_audit_log(
        db,
        action="users.update",
        entity_type="users",
        entity_id=updated_user.id,
        entity_label=updated_user.username,
        actor=current_user,
        request=request,
        before_data=before_data,
        after_data=updated_user,
    )
    return updated_user


@router.put("/{user_id}/password", response_model=UserResponse)
def update_user_password(
    user_id: int,
    password_update: UserPasswordUpdate,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.password.update")),
):
    db_user = get_user_or_404(db, user_id)
    before_data = sanitize(db_user)

    logger.info(
        "users.password_update",
        extra={"actor_user_id": current_user.id, "target_user_id": user_id},
    )
    updated_user = UserRepository.update_password(db, db_user, password_update)
    write_audit_log(
        db,
        action="users.password_update",
        entity_type="users",
        entity_id=updated_user.id,
        entity_label=updated_user.username,
        actor=current_user,
        request=request,
        before_data=before_data,
        after_data=updated_user,
        metadata={"password_changed": True},
    )
    return updated_user


@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(
    user_id: int,
    request: Request,
    db: Session = Depends(get_db),
    current_user: User = Depends(require_permission("users.delete")),
):
    if current_user.id == user_id:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail="Der aktuell angemeldete Benutzer darf sich nicht selbst löschen",
        )

    db_user = get_user_or_404(db, user_id)
    before_data = sanitize(db_user)
    entity_label = db_user.username

    logger.info(
        "users.delete",
        extra={"actor_user_id": current_user.id, "target_user_id": user_id},
    )
    UserRepository.delete(db, db_user)
    write_audit_log(
        db,
        action="users.delete",
        entity_type="users",
        entity_id=user_id,
        entity_label=entity_label,
        actor=current_user,
        request=request,
        before_data=before_data,
    )
    return Response(status_code=status.HTTP_204_NO_CONTENT)
