import warnings
import logging



def remove_forced(logger, model, universe, force_removal):
    
    
    if force_removal != '-':
        removed_rids = []
        forced_rids = force_removal.split(',')
        
        modeled_rids = [r.id for r in model.reactions]
        universal_rids = [r.id for r in universe.reactions]
        
        for rid in forced_rids: 
            
            if rid not in universal_rids:
                logger.info(f"Ignoring reaction ID '{rid}' since it's not included in the universe.")
                continue
            
            if rid not in modeled_rids:
                logger.debug(f"Requested reaction ID '{rid}' was already excluded from the model.")
            else:
                removed_rids.append(rid)

        # remove collected reactions:
        with warnings.catch_warnings():
            # avoid warnings like "python3.9/site-packages/cobra/core/group.py:147: UserWarning: need to pass in a list"
            model.remove_reactions(removed_rids) # this works also with rids instaead of Rs

        logger.info(f"Reactions forcibly removed: {removed_rids}.")
        


def remove_disconnected(logger, model):
    
    
    # remove metabolites involved in 0 reactions:
    to_remove = []
    for m in model.metabolites:
        if len(m.reactions) == 0:
            to_remove.append(m)
    model.remove_metabolites(to_remove)
    
    
    # return number of removed metabolites
    return len(to_remove)

    
    
def remove_orphans(logger, model, keep_transport):
    
    
    # collect universal orphans
    to_remove = []
    for r in model.reactions:       
        if r.id == 'Biomass': 
            continue   # exclude biomass
        if len(r.metabolites) == 1: 
            continue   # exclude exr, sinks, demands 
        if keep_transport and len(set([m.id.rsplit('_',1)[-1] for m in r.metabolites])) > 1: 
            continue  # exclude transport reactions if requested
        if r.gene_reaction_rule == '': 
            logger.debug(f"Removing orphan '{r.id}'...")
            to_remove.append(r.id)
            
    
    # trick to avoid the WARNING "cobra/core/group.py:147: UserWarning: need to pass in a list" 
    # triggered when trying to remove reactions that are included in groups. 
    with warnings.catch_warnings():  # temporarily suppress warnings for this block
        warnings.simplefilter("ignore")  # ignore all warnings
        cobra_logger = logging.getLogger("cobra.util.solver")
        old_level = cobra_logger.level
        cobra_logger.setLevel(logging.ERROR)   

        model.remove_reactions(to_remove) 

        # restore original behaviour: 
        cobra_logger.setLevel(old_level)   
            

    # return number of removed reactions
    return len(to_remove)



def remove_sinks_demands(logger, model, keep_meanfa=False):
    
    
    # collect universal orphans
    to_remove = []
    for r in model.reactions:
        if r.id == 'Biomass': continue   # exclude biomass
        if len(r.metabolites) != 1: continue  # exclude transport and non-transport reactions
        if len(r.metabolites) == 1 and list(r.metabolites)[0].id.endswith('_e'): continue   # exclude EXR
        if r.id == 'sn_meanfa_c' and keep_meanfa: continue
        if r.gene_reaction_rule == '': 
            # should remain only sinks/demands
            logger.debug(f"Removing sink/demand '{r.id}'...")
            to_remove.append(r.id)
            
    
    # trick to avoid the WARNING "cobra/core/group.py:147: UserWarning: need to pass in a list" 
    # triggered when trying to remove reactions that are included in groups. 
    with warnings.catch_warnings():  # temporarily suppress warnings for this block
        warnings.simplefilter("ignore")  # ignore all warnings
        cobra_logger = logging.getLogger("cobra.util.solver")
        old_level = cobra_logger.level
        cobra_logger.setLevel(logging.ERROR)   

        model.remove_reactions(to_remove) 

        # restore original behaviour: 
        cobra_logger.setLevel(old_level)   
            

    # return number of removed reactions
    return len(to_remove)
    
    
    