import os

from glob import glob
from bidict import OrderedBidict
from simplejson import OrderedDict
import ruamel.yaml

import FAIRsoft
from FAIRsoft import utils
import bioconda_utils.recipe as brecipe # this module is installed in env 'bioconda'
import dotenv

dotenv.load_dotenv()


class Error(Exception):
    """Base class for other exceptions"""
    pass

def run_host_target(tool, type_):
    if tool['requirements'].get('host'):
        for host in tool['requirements']['host']:
            if True in [lang in host for lang in ['r-base', 'python','perl'] ]:
                if tool['requirements'].get('run'):
                    for run in tool['requirements']['run']:
                        if True in [lang in run for lang in ['r-base', 'python','perl'] ]:
                            type_.add('lib')
    return(type_)
    


def get_type(tool):
    '''
    If bioconductor in name and urls:
        Lib
    If test:
        If test.imports: 
            Lib
        If test.commands: 
            CMD
        If source_files:
            CMD
    Else:
        If build.entrypoints: 
            CMD
        Else-If (python OR perl OR r-base) in requirements.host:
            If (python OR perl OR r-base) in requirements.run:
                Lib
        Else:
            CMD
    '''
    type_ = set()
    bioconductor = False

    if 'bioconductor-' in tool['name']:
        bioconductor = True
    if tool.get('source'):
        # source can be a list or a dict. The following handles it

        if isinstance(tool.get('source'), ruamel.yaml.comments.CommentedMap):
            source = [tool.get('source')]    
        elif isinstance(tool.get('source'), ruamel.yaml.comments.CommentedSeq):
            source = tool.get('source')
        else:
            source = []
            #print(type(tool.get('source')))
            #print(len(tool.get('source')))

        # source = list(s1, s2, ....)
        for l in source:
            if l.get('url'):
                for url in l['url']:
                    if 'bioconductor' in url:
                        bioconductor = True

    if tool.get('about'):
        if tool['about'].get('home'):
            if 'bioconductor' in tool['about']['home']:
                bioconductor = True

    if bioconductor == True:
        type_.add('lib')

    if tool.get('test'):
        if 'commands' in tool['test']:
            if tool['test']['commands']:
                if True not in ['$R' in command for command in tool['test']['commands']]:
                    type_.add('cmd')
        if 'imports' in tool['test']:
            if tool['test']['imports']:
                type_.add('lib')
        if 'source_files' in tool['test']:
            if tool['test']['source_files']:
                type_.add('cmd')

    else:
        if tool.get('build'):
            if tool['build'].get('entrypoints'):
                type_.add('cmd')
        elif tool.get('requirements'):
            type_ = run_host_target(tool, type_)
            
    if len(type_) == 0:
        type_.add('cmd')

    return(type_)

def build_id(tool):
    id_template = "https://openebench.bsc.es/monitor/tool/bioconda_recipes:{name}:{version}/{type}"
    name = tool['package']['name']
    type_= list(get_type(tool))
    tool['type'] = type_
    id_ = id_template.format(name=tool['name'], version=None, type=type_)
    return(id_)

def get_tool_names(subdirectories):
    tool_names_subs = []
    tool_names_subs_raw = []
    bioconductors = 0
    for directory in subdirectories:
        name = directory.split('/')[-2]
        tool_names_subs_raw.append(name)
    return(tool_names_subs_raw)

class NonPackageError(Error):
    """Raised when a meta.yml is not a package. Usually generated by other recipes"""
    pass

def extract_metadata(package, recipes_path, log):
    insts = []
    try:
        recipe = brecipe.load_parallel_iter(recipes_path, package)
    except Exception as e:
        log['errors'].append({'package':package,'error':e})
        return(insts, log)
    else:
        for a in recipe:
            a.render
            a.meta['name'] = package
            if a.meta.get('package'):
                if a.meta['package'].get('name'):
                    a.meta['name']=a.meta['package']['name']
            
            insts.append(a.meta)
        
        return(insts, log)


def retrieve_packages_metadata(tool, recipes_path, log):
    inst_dicts, log = extract_metadata(tool,recipes_path, log)
    
    STORAGE_MODE = os.getenv('STORAGE_MODE', 'db')
    ALAMBIQUE = os.getenv('ALAMBIQUE', 'alambique')

    if STORAGE_MODE =='db':
        alambique = FAIRsoft.utils.connect_collection(ALAMBIQUE)
    else:
        OUTPUT_PATH = os.getenv('OUTPUT_PATH', './')
        OUTPUT_BIOCONDA_RECIPES = os.getenv('OUTPUT_BIOCONDA_RECIPES', 'bioconda_recipes.json')
        output_file = OUTPUT_PATH + '/' + ALAMBIQUE + '/' + OUTPUT_BIOCONDA_RECIPES

    if inst_dicts:
        for inst_dict in inst_dicts:
            if inst_dict:
                inst_dict['@id'] = build_id(inst_dict)  
                inst_dict['@data_source'] = 'bioconda_recipes'
                if STORAGE_MODE=='db':
                    try:
                        updateResult = alambique.update_one({'@id':tool['@id']}, { '$set': tool }, upsert=True)
                    except Exception as e:
                        log['errors'].append({'file':tool,'error':e})
        
                    else:
                        log['n_ok'] += 1
                else:
                    log = FAIRsoft.utils.save_entry(inst_dict, output_file, log)
    else:
        log['errors'].append({'file':tool,'error':'Empty inst_dict'}) 
    return(log)

def print_progress(log):
    # Keeping track of progress
    #print(f"{len(log['names'])} packages processes --- {log['n_ok']} parsed and loaded sucessfully --- {len(log['errors'])} raised an exception", end="\r", flush=True)
    pass

def process_recipes():
    # 3. List tool names in the directory
    try:
        recipes_path = os.getenv('RECIPES_PATH')        
        print(recipes_path)
    except:
        raise Exception('RECIPES_PATH environment variable not set. Exiting importation.')
    else:
        subdirectories = glob("%s/*/"%(recipes_path))
        tool_names_subs_raw = get_tool_names(subdirectories)
        print('List of names obtained')
        print('Number of tools: %s'%(len(tool_names_subs_raw)))
    
        # For each tool
        print('Processing metadata')
        log = {'names':[],
                'n_ok':0,
                'errors': []}
        for tool in tool_names_subs_raw:
            # 4. Process and push metadata
            print(tool)
            log = retrieve_packages_metadata(tool, recipes_path, log)
            print_progress(log)

    
    print(f"----- Importation finished ----- \n Number of packages in Bioconda {len(log['names'])}")
    print('Exceptions\n')
    for e in log['errors']:
        print(e['error'])  
    


if __name__=='__main__':
    # Extract and push metadata
    process_recipes()


