import copy


def validate_variable_xpaths(sed_variables, model_etree):
    """ Get the names OpenCOR uses to refer to model variable

    Args:
        model_etree (:obj:`lxml.etree._ElementTree`): element tree for model
        sed_variables (:obj:`list` of :obj:`Variable`): SED variables

    Returns:
        :obj:`dict`: dictionary that maps the id of each SED variable to the name that OpenCOR uses to reference it
    """
    opencor_variable_names = {}
    for sed_variable in sed_variables:
        if not sed_variable.target:
            msg = 'Symbols are not supported.'
            raise NotImplementedError(msg)

        namespaces = copy.copy(sed_variable.target_namespaces)
        namespaces.pop(None, None)

        obj_target, _, attrib_target = sed_variable.target.partition('/@')
        xml_objs = model_etree.xpath(obj_target, namespaces=namespaces)

        if len(xml_objs) == 0:
            msg = (
                'XPath targets of variables must reference unique observables. '
                'The target `{}` of variable `{}` does not match any model elements.'
            ).format(sed_variable.target, sed_variable.id)
            raise ValueError(msg)

        if len(xml_objs) > 1:
            msg = (
                'XPath targets of variables must reference unique observables. '
                'The target `{}` of variable `{}` matches multiple model elements.'
            ).format(sed_variable.target, sed_variable.id)
            raise ValueError(msg)

        xml_obj = xml_objs[0]

        names = []
        while True:
            name = xml_obj.attrib.get('name', None)
            names.append(name)
            xml_obj = xml_obj.getparent()
            ns, _, tag = xml_obj.tag[1:].partition('}')
            if not name or not ns.startswith('http://www.cellml.org/cellml/'):
                msg = 'Target `{}` of variable `{}` is not a valid observable.'.format(sed_variable.target, sed_variable.id)
                raise ValueError(msg)
            if tag == 'model':
                break

        if attrib_target:
            names.insert(0, attrib_target)
        opencor_variable_names[sed_variable.id] = '/'.join(reversed(names))

    return opencor_variable_names