"""
Public User Tracking SDK Version 2

This module provides a public interface for tracking user events.
This replaces the previous version of the SDK available in ddtrace.appsec.trace_utils
Implementation can change in the future, but the interface will remain compatible.
"""

import typing as t

from ddtrace.appsec import _asm_request_context
from ddtrace.appsec import _constants
from ddtrace.appsec import _metrics
from ddtrace.appsec import _trace_utils
from ddtrace.appsec._asm_request_context import get_blocked as _get_blocked
from ddtrace.appsec._constants import WAF_ACTIONS as _WAF_ACTIONS
import ddtrace.appsec.trace_utils  # noqa: F401
from ddtrace.internal._exceptions import BlockingException


def track_login_success(
    login: str, user_id: t.Any = None, metadata: t.Optional[t.Dict[str, t.Any]] = None, _auto: bool = False
) -> None:
    """
    Track a successful user login event.

    This function should be called when a user successfully logs in to the application.
    It will create an event that can be used for monitoring and analysis.
    """
    _metrics._report_ato_sdk_usage("login_success")
    mode = _constants.LOGIN_EVENTS_MODE.AUTO if _auto else _constants.LOGIN_EVENTS_MODE.SDK
    _trace_utils.track_user_login_success_event(None, user_id, login=login, metadata=metadata, login_events_mode=mode)


def track_login_failure(
    login: str,
    exists: bool,
    user_id: t.Any = None,
    metadata: t.Optional[t.Dict[str, t.Any]] = None,
    _auto: bool = False,
):
    """
    Track a failed user login event.

    This function should be called when a user fails to log in to the application.
    It will create an event that can be used for monitoring and analysis.
    """
    _metrics._report_ato_sdk_usage("login_failure")
    mode = _constants.LOGIN_EVENTS_MODE.AUTO if _auto else _constants.LOGIN_EVENTS_MODE.SDK
    _trace_utils.track_user_login_failure_event(
        None, user_id, exists=exists, login=login, metadata=metadata, login_events_mode=mode
    )


def track_signup(
    login: str,
    user_id: t.Any = None,
    success: bool = True,
    metadata: t.Optional[t.Dict[str, t.Any]] = None,
    _auto: bool = False,
):
    """
    Track a user signup event.

    This function should be called when a user successfully signs up for the application.
    It will create an event that can be used for monitoring and analysis.
    """
    _metrics._report_ato_sdk_usage("signup")
    mode = _constants.LOGIN_EVENTS_MODE.AUTO if _auto else _constants.LOGIN_EVENTS_MODE.SDK
    _trace_utils.track_user_signup_event(None, user_id, success, login=login, login_events_mode=mode)
    if metadata:
        _trace_utils.track_custom_event(None, "signup_sdk", metadata=metadata)


def track_user(
    login: str,
    user_id: t.Any = None,
    session_id: t.Optional[str] = None,
    metadata: t.Optional[t.Dict[str, t.Any]] = None,
    _auto: bool = False,
):
    """
    Track an authenticated user.

    This function should be called when a user is authenticated in the application."
    """
    span = _asm_request_context.get_entry_span()
    if span is None:
        return
    if user_id:
        span._set_tag_str(_constants.APPSEC.USER_LOGIN_USERID, str(user_id))
    if login:
        span._set_tag_str(_constants.APPSEC.USER_LOGIN_USERNAME, str(login))
    meta = metadata or {}
    usr_name = meta.pop("name", None) or meta.pop("usr.name", None)
    usr_email = meta.pop("email", None) or meta.pop("usr.email", None)
    usr_scope = meta.pop("scope", None) or meta.pop("usr.scope", None)
    usr_role = meta.pop("role", None) or meta.pop("usr.role", None)
    _trace_utils.set_user(
        None,
        user_id,
        name=usr_name if isinstance(usr_name, str) else None,
        email=usr_email if isinstance(usr_email, str) else None,
        scope=usr_scope if isinstance(usr_scope, str) else None,
        role=usr_role if isinstance(usr_role, str) else None,
        session_id=session_id,
        span=span,
        may_block=_auto,
        mode=_constants.LOGIN_EVENTS_MODE.AUTO if _auto else _constants.LOGIN_EVENTS_MODE.SDK,
    )
    if meta:
        _trace_utils.track_custom_event(None, "auth_sdk", metadata=meta)
    if not _auto:
        span._set_tag_str(_constants.APPSEC.AUTO_LOGIN_EVENTS_COLLECTION_MODE, _constants.LOGIN_EVENTS_MODE.SDK)
        if _asm_request_context.in_asm_context():
            custom_data = {
                "REQUEST_USER_ID": str(user_id) if user_id else None,
                "REQUEST_USERNAME": login,
                "LOGIN_SUCCESS": "sdk",
            }
            if session_id:
                custom_data["REQUEST_SESSION_ID"] = session_id
            res = _asm_request_context.call_waf_callback(custom_data=custom_data, force_sent=True)
            if res and any(
                action in [_WAF_ACTIONS.BLOCK_ACTION, _WAF_ACTIONS.REDIRECT_ACTION] for action in res.actions
            ):
                raise BlockingException(_get_blocked())


def track_user_id(
    user_id: t.Any,
    session_id: t.Optional[str] = None,
    metadata: t.Optional[t.Dict[str, t.Any]] = None,
    _auto: bool = False,
):
    """
    Track an authenticated user with only user id.

    This function should be called when a user is authenticated in the application."
    """
    span = _asm_request_context.get_entry_span()
    if span is None:
        return
    if user_id:
        span._set_tag_str(_constants.APPSEC.USER_LOGIN_USERID, str(user_id))
    meta = metadata or {}
    usr_name = meta.pop("name", None) or meta.pop("usr.name", None)
    usr_email = meta.pop("email", None) or meta.pop("usr.email", None)
    usr_scope = meta.pop("scope", None) or meta.pop("usr.scope", None)
    usr_role = meta.pop("role", None) or meta.pop("usr.role", None)
    _trace_utils.set_user(
        None,
        user_id,
        name=usr_name if isinstance(usr_name, str) else None,
        email=usr_email if isinstance(usr_email, str) else None,
        scope=usr_scope if isinstance(usr_scope, str) else None,
        role=usr_role if isinstance(usr_role, str) else None,
        session_id=session_id,
        span=span,
        may_block=_auto,
        mode=_constants.LOGIN_EVENTS_MODE.AUTO if _auto else _constants.LOGIN_EVENTS_MODE.SDK,
    )
    if meta:
        _trace_utils.track_custom_event(None, "auth_sdk", metadata=meta)
    if not _auto:
        span._set_tag_str(_constants.APPSEC.AUTO_LOGIN_EVENTS_COLLECTION_MODE, _constants.LOGIN_EVENTS_MODE.SDK)
        if _asm_request_context.in_asm_context():
            custom_data = {
                "REQUEST_USER_ID": str(user_id) if user_id else None,
                "LOGIN_SUCCESS": "sdk",
            }
            if session_id:
                custom_data["REQUEST_SESSION_ID"] = session_id
            res = _asm_request_context.call_waf_callback(custom_data=custom_data, force_sent=True)
            if res and any(
                action in [_WAF_ACTIONS.BLOCK_ACTION, _WAF_ACTIONS.REDIRECT_ACTION] for action in res.actions
            ):
                raise BlockingException(_get_blocked())


def track_custom_event(event_name: str, metadata: t.Dict[str, t.Any]):
    """
    Track a custom user event.

    This function should be called when a custom user event occurs in the application.
    It will create an event that can be used for monitoring and analysis.
    """
    _metrics._report_ato_sdk_usage("custom")
    _trace_utils.track_custom_event(None, event_name, metadata=metadata)
