from typing import Union
from typing import Dict
from typing import Optional


def get_prefixed_name(
        qname: str, namespaces: Union[Dict[str, str], Dict[Optional[str], str]]) -> str:
    """
    Get the prefixed form of a QName, using a namespace map.

    :param qname: an extended QName or a local name or a prefixed QName.
    :param namespaces: a dictionary with a map from prefixes to namespace URIs.
    """
    try:
        if qname[0] == '{':
            ns_uri, local_name = qname[1:].split('}')
        elif qname[1] == '{' and qname[0] == 'Q':
            ns_uri, local_name = qname[2:].split('}')
        else:
            return qname
    except IndexError:
        return qname
    except (ValueError, TypeError):
        raise ValueError("{!r} is not a QName".format(qname))

    for prefix, uri in sorted(namespaces.items(), reverse=True,
                              key=lambda x: x if x[0] is not None else ('', x[1])):
        if uri == ns_uri:
            return '%s:%s' % (prefix, local_name) if prefix else local_name
    else:
        return qname