from warnings import warn


def check_boundary_metabolites(model, extracellular='e', to_compartment='c'):
    """ Check that all boundary metabolites have a path from the boundary to a specific compartment.

    Parameters
    ----------
    model : cobra.core.Model
        Model to check
    extracellular : str, optional
        ID of extracellular compartment (the compartment at the boundary)
    to_compartment : str, optional
        ID of specific compartment (typically cytosol)
    """

    # Get the list of metabolites in the extracellular compartment.
    metabolites = model.metabolites.query(lambda x: x == extracellular, 'compartment')

    # Look for reactions that move a metabolite from the boundary to the
    # specified compartment.
    num_valid = 0
    for met in metabolites:
        exchange_id = 'EX_{}'.format(met.id)  # Assume standard naming convention
        try:
            rxn = model.reactions.get_by_id(exchange_id)
            if not rxn.boundary:
                warn('Reaction {0} for metabolite {1} is not an exchange reaction'
                     .format(rxn.id, met.name))
            if rxn.lower_bound == 0. or rxn.upper_bound == 0.:
                warn('Exchange reaction {0} for metabolite {1} has invalid bounds {2}'
                     .format(rxn.id, met.name, rxn.bounds))
        except KeyError:
            warn('Exchange reaction missing for metabolite {0} ({1})'.format(met.id, met.name))
        transport = False
        for rxn in met.reactions:
            if len(rxn.compartments) > 1:
                if extracellular in rxn.compartments and to_compartment in rxn.compartments:
                    transport = True
                    if rxn.lower_bound == 0. or rxn.upper_bound == 0.:
                        warn('Transport reaction {0} for metabolite {1} has invalid bounds {2}'
                             .format(rxn.id, met.name, rxn.bounds))
        if not transport:
            warn('Metabolite {0} ({1}) has no transport reaction to compartment {2}'
                 .format(met.id, met.name, to_compartment))
        else:
            num_valid += 1

    if len(metabolites) - num_valid != 0:
        warn('{0} metabolites do not have a path from boundary to comparment {1}'
             .format(len(metabolites) - num_valid, to_compartment))
    return num_valid