#!/usr/bin/env python

""" MultiQC code to export data to MegaQC / flat JSON files """

from __future__ import print_function
import gzip
import io
import json
import os
import requests

from multiqc import config
log = config.logger

# Custom encoder to handle lambda functions
class MQCJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if callable(obj):
            try:
                return obj(1)
            except:
                return None
        return json.JSONEncoder.default(self, obj)

def multiqc_dump_json(report):
    exported_data = dict()
    export_vars = {
        'report': [
            'data_sources',
            'general_stats_data',
            'general_stats_headers',
            'multiqc_command',
            'plot_data',
            'saved_raw_data',
        ],
        'config': [
            'analysis_dir',
            'creation_date',
            'git_hash',
            'intro_text',
            'report_comment',
            'report_header_info',
            'script_path',
            'short_version',
            'subtitle',
            'title',
            'version',
        ]
    }
    for s in export_vars:
        for k in export_vars[s]:
            try:
                if s == 'config':
                    d = {'{}_{}'.format(s, k): getattr(config, k)}
                elif s == 'report':
                    d = {'{}_{}'.format(s, k): getattr(report, k)}
                json.dumps(d, cls=MQCJSONEncoder, ensure_ascii=False) # Test that exporting to JSON works
                exported_data.update(d)
            except (TypeError, KeyError, AttributeError):
                log.warning("Couldn't export data key '{}.{}'".format(s, k))
        # Get the absolute paths of analysis directories
        exported_data['config_analysis_dir_abs'] = list()
        for d in exported_data.get('config_analysis_dir', []):
            try:
                exported_data['config_analysis_dir_abs'].append(os.path.abspath(d))
            except:
                pass
    return exported_data


def multiqc_api_post(exported_data):
    headers = { 'Content-Type': 'application/json', 'content-encoding': 'gzip' }
    if config.megaqc_access_token is not None:
        headers['access_token'] = config.megaqc_access_token
    post_data = json.dumps({'data': exported_data}, cls=MQCJSONEncoder, ensure_ascii=False, indent=2)
    post_data = post_data.encode('utf-8', 'ignore')

    # Gzip the JSON for massively decreased filesize
    sio_obj = io.BytesIO()
    gzfh = gzip.GzipFile(fileobj=sio_obj, mode='w')
    gzfh.write(post_data)
    gzfh.close()
    request_body = sio_obj.getvalue()

    log.debug("Sending data to MegaQC")
    log.debug("MegaQC URL: {}".format(config.megaqc_url))
    try:
        r = requests.post(config.megaqc_url, headers=headers, data=request_body, timeout=config.megaqc_timeout)
    except (requests.exceptions.ConnectTimeout, requests.exceptions.ReadTimeout) as e:
        log.error("Timed out when sending data: {}".format(e))
    except requests.exceptions.ConnectionError:
        log.error("Couldn't connect to MegaQC URL {}".format(config.megaqc_url))
    except Exception as e:
        log.error("Error sending data: {}".format(e))
    else:
        try:
            api_r = json.loads(r.text)
        except Exception as e:
            log.error('Error: JSON response could not be parsed (status code: {})'.format(r.status_code))
            return None
        if r.status_code == 200:
            if api_r['success']:
                log.info('{}'.format(api_r['message']))
            else:
                log.error('Error - {}'.format(api_r['message']))
        else:
            if r.status_code == 403:
                if config.megaqc_access_token is not None:
                    log.error('Error 403: Authentication error, megaqc_access_token not recognised')
                else:
                    log.error('Error 403: Authentication error, megaqc_access_token is required')
            else:
                log.debug("MegaQC API status code was {}".format(r.status_code))
                log.error('Error - {}'.format(api_r.get('message', 'Unknown problem')))
