from typing import Mapping
from typing import Optional
from typing import Callable
from typing import Tuple
from typing import Any
import re
from typing import Iterable


def validate_mapping(item: Mapping,
                     key_types: tuple,
                     value_types: tuple,
                     key_str_validator: Optional[str] = None,
                     iterable_validator: Optional[Callable] = None
                     ) -> Tuple[int, Any]:
    """
    Validates mapping items
    :returns: (int - status code;
               Any - invalid item, None otherwise)
    Status codes:
    0 - success
    1 - type error
    2 - format error
    """
    if not isinstance(item, Mapping):
        return 1, item

    for k, v in item.items():
        if not isinstance(k, key_types):
            return 1, k

        if key_str_validator is not None and isinstance(k, str):
            if not re.match(key_str_validator, k):
                return 2, k

        if not isinstance(v, value_types):
            return 1, v

        if isinstance(v, Mapping):
            mapping_v_res, mapping_v_res_item = validate_mapping(
                v,
                key_types,
                value_types,
                key_str_validator,
                iterable_validator)
            if mapping_v_res > 0:
                return mapping_v_res, mapping_v_res_item
        elif iterable_validator is not None \
                and not isinstance(v, str) and isinstance(v, Iterable):
            iter_v_res, iter_v_res_item = iterable_validator(v)
            if iter_v_res > 0:
                return 1, iter_v_res_item

    return 0, None