import logging
from typing import Annotated

from arthur_common.models.common_schemas import PaginationParameters, UserPermission
from arthur_common.models.enums import (
    UserPermissionAction,
    UserPermissionResource,
)
from arthur_common.models.request_schemas import CreateUserRequest, PasswordResetRequest
from arthur_common.models.response_schemas import UserResponse
from fastapi import APIRouter, Depends, HTTPException, Path, Query, Response
from sqlalchemy.orm import Session
from starlette import status

from auth.ApiKeyValidator.APIKeyvalidatorCreator import APIKeyValidatorCreator
from auth.ApiKeyValidator.enums import APIKeyValidatorType
from auth.multi_validator import MultiMethodValidator
from auth.oauth_validator import validate_token
from clients.auth.abc_keycloak_client import ABCAuthClient
from dependencies import get_db_session, get_keycloak_client
from repositories.organizations_repository import OrganizationsRepository
from routers.route_handler import GenaiEngineRoute
from routers.v2 import multi_validator as v2_multi_validator
from schemas.enums import PermissionLevelsEnum
from schemas.internal_schemas import User
from schemas.response_schemas import MeResponse, OrganizationResponse
from utils.users import permission_checker
from utils.utils import common_pagination_parameters, constants, public_endpoint

api_key_validator_creators = [
    APIKeyValidatorCreator(APIKeyValidatorType.USER_GEN),
]
multi_validator = MultiMethodValidator(api_key_validator_creators)
logger = logging.getLogger()

user_management_routes = APIRouter(
    prefix="/users",
    route_class=GenaiEngineRoute,
    tags=["User Management"],
)

# Separate router for self-identity endpoints so they remain reachable even
# when API-only mode is enabled (which gates the rest of user_management).
user_identity_routes = APIRouter(
    prefix="/users",
    route_class=GenaiEngineRoute,
    tags=["User Management"],
)


@user_management_routes.post(
    "",
    description=f"Creates a new user with specific roles. The available roles are {constants.TASK_ADMIN} "
    f"and {constants.CHAT_USER}. The 'temporary' field is for indicating if the user password needs to be reset at the first login.",
)
@permission_checker(permissions=PermissionLevelsEnum.USER_WRITE.value)
def create_user(
    request: CreateUserRequest,
    kc_client: ABCAuthClient = Depends(get_keycloak_client),
    current_user: User | None = Depends(multi_validator.validate_api_multi_auth),
) -> Response:
    kc_client.create_user(request)  # type: ignore[arg-type]
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@user_management_routes.get(
    "",
    description=f"Fetch users.",
    response_model=list[UserResponse],
)
@permission_checker(permissions=PermissionLevelsEnum.USER_READ.value)
def search_users(
    pagination_parameters: Annotated[
        PaginationParameters,
        Depends(common_pagination_parameters),
    ],
    search_string: Annotated[
        str | None,
        Query(
            description="Substring to match on. Will search first name, last name, email.",
        ),
    ] = None,
    kc_client: ABCAuthClient = Depends(get_keycloak_client),
    current_user: User | None = Depends(multi_validator.validate_api_multi_auth),
) -> list[UserResponse]:
    users = kc_client.search_users(
        search_string=search_string or "",
        page=pagination_parameters.page,
        page_size=pagination_parameters.page_size,
    )
    return [user._to_response_model() for user in users]


@user_identity_routes.get(
    "/me",
    description=(
        "Returns the current caller's identity, roles, org_scope, and (when "
        "scoped) the organization record. Used by the UI on login to decide "
        "between admin and tenant render branches. Admin callers and JWT "
        "callers receive org_scope=null and org=null."
    ),
    response_model=MeResponse,
)
@public_endpoint
def get_me(
    current_user: User | None = Depends(v2_multi_validator.validate_api_multi_auth),
    db_session: Session = Depends(get_db_session),
) -> MeResponse:
    if not current_user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=constants.ERROR_AUTHENTICATION_REQUIRED,
            headers={"full_stacktrace": "false"},
        )

    org: OrganizationResponse | None = None
    if current_user.org_scope is not None:
        db_org = OrganizationsRepository(db_session).get_organization_by_id(
            current_user.org_scope,
        )
        if db_org is not None:
            org = OrganizationResponse(id=db_org.id, name=db_org.name)

    return MeResponse(
        user_id=current_user.id,
        roles=[role.name for role in current_user.roles],
        org_scope=current_user.org_scope,
        org=org,
    )


@user_management_routes.get(
    "/permissions/check",
    description=f"Checks if the current user has the requested permission. Returns 200 status code for authorized or 403 if not.",
)
@public_endpoint
def check_user_permission(
    current_user: User | None = Depends(multi_validator.validate_api_multi_auth),
    action: UserPermissionAction = Query(
        None,
        description="Action to check permissions of.",
    ),
    resource: UserPermissionResource = Query(
        None,
        description="Resource to check permissions of.",
    ),
    kc_client: ABCAuthClient = Depends(get_keycloak_client),
) -> Response:
    if not current_user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=constants.ERROR_AUTHENTICATION_REQUIRED,
            headers={"full_stacktrace": "false"},
        )
    if not action and not resource:
        raise HTTPException(
            status_code=400,
            detail=constants.ERROR_ACTION_AND_RESOURCE_REQUIRED,
        )
    if kc_client.check_user_permissions(
        current_user.id,
        UserPermission(action=action, resource=resource),
    ):
        return Response(status_code=status.HTTP_200_OK)
    else:
        return Response(status_code=403)


@user_management_routes.delete("/{user_id}", description="Delete a user.")
@permission_checker(permissions=PermissionLevelsEnum.USER_WRITE.value)
def delete_user(
    user_id: Annotated[
        str,
        Path(description="User id, not email."),
    ],
    kc_client: ABCAuthClient = Depends(get_keycloak_client),
    current_user: User | None = Depends(multi_validator.validate_api_multi_auth),
) -> Response:
    kc_client.delete_user(user_id)
    return Response(status_code=status.HTTP_204_NO_CONTENT)


@user_management_routes.post(
    "/{user_id}/reset_password",
    description="Reset password for user.",
)
@permission_checker(permissions=PermissionLevelsEnum.PASSWORD_RESET.value)
def reset_user_password(
    user_id: str,
    request_body: PasswordResetRequest,
    current_user: Annotated[User, Depends(validate_token)],
    kc_client: ABCAuthClient = Depends(get_keycloak_client),
) -> None:
    if (
        constants.ORG_ADMIN in [role.name for role in current_user.roles]
        or user_id == current_user.id
    ):
        kc_client.reset_password(user_id=user_id, new_password=request_body.password)
    else:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="You can't reset password for this user.",
        )
