def parse_join_type(join_type: str) -> str:
    """Parse and normalize join type string. The normalization
    will lower the string, remove all space and ``_``, and then
    map to the limited options.

    Here are the options after normalization: ``inner``, ``cross``,
    ``left_semi``, ``left_anti``, ``left_outer``, ``right_outer``,
    ``full_outer``.

    :param join_type: the raw join type string
    :raises NotImplementedError: if not supported
    :return: the normalized join type string
    """
    join_type = join_type.replace(" ", "").replace("_", "").lower()
    if join_type in ["inner", "cross"]:
        return join_type
    if join_type in ["inner", "join"]:
        return "inner"
    if join_type in ["semi", "leftsemi"]:
        return "left_semi"
    if join_type in ["anti", "leftanti"]:
        return "left_anti"
    if join_type in ["left", "leftouter"]:
        return "left_outer"
    if join_type in ["right", "rightouter"]:
        return "right_outer"
    if join_type in ["outer", "full", "fullouter"]:
        return "full_outer"
    raise NotImplementedError(join_type)