import os
from functools import reduce

import boto3
import yaml
from copy import deepcopy
from cryptography.fernet import Fernet

from pycbc import json
from pycbc.utils import AttrDict as d

s3 = boto3.client('s3')
_mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
_DEFAULTS = d({
    'users': [],
    'encrypt_key': Fernet.generate_key().decode('utf-8'),
    'api_gateway': None,
    'sender_email': None,
    'logging': d({
        'version': 1,
        'formatters': d({
            'default': d({
                'format': '%(asctime)-15s - %(levelname)-7s - %(message)s',
            }),
        }),
        'handlers': d({
            'console': d({
                'class': 'logging.StreamHandler',
                'formatter': 'default',
                'level': 'DEBUG',
                'stream': 'ext://sys.stderr',
            }),
        }),
        'loggers': d({
            'pycbc': d({
                'handlers': ['console'],
                'level': 'INFO',
            })
        })
    })
})


def load(event):
    event_override = event.get('config', d())
    env_prefix = event_override.get(
        'env_prefix', os.getenv('ENV_PREFIX', 'PYCBC_'))
    s3_bucket = event_override.get(
        's3_bucket', os.getenv(f'{env_prefix}S3_BUCKET', 'pycbc'))
    s3_filename = event_override.get(
        's3_filename',
        os.getenv(f'{env_prefix}S3_FILENAME', 'pycbc-config.yaml')
    )
    return json.loads(json.dumps(reduce(
        _merge,
        [
            deepcopy(_DEFAULTS),
            _from_s3(s3_bucket, s3_filename),
            _from_env(env_prefix),
            event_override,
            {'s3_bucket': s3_bucket, 's3_filename': s3_filename}
        ])
    ))


def _merge(a, b, path=None):
    if path is None:
        path = []
    for key in b:
        if key in a:
            if isinstance(a[key], dict) and isinstance(b[key], dict):
                _merge(a[key], b[key], path + [str(key)])
            else:
                a[key] = b[key]

        else:
            a[key] = b[key]
    return a


def _yaml_load(data):
    yaml.add_constructor(
        _mapping_tag,
        lambda loader, node: d(loader.construct_pairs(node)),
    )
    return yaml.load(data, Loader=yaml.FullLoader)


def _from_env(prefix):
    env_vars = (k for k in os.environ if k.startswith(prefix))
    return d({
        k[len(prefix):].lower(): os.environ[k] for k in env_vars
    })


def _from_s3(bucket, filename):
    fileobj = s3.get_object(
        Bucket=bucket,
        Key=filename,
    )
    return _yaml_load(fileobj['Body'].read())

[((12, 5, 12, 23), 'boto3.client', 'boto3.client', ({(12, 18, 12, 22): '"""s3"""'}, {}), "('s3')", False, 'import boto3\n'), ((86, 11, 86, 50), 'yaml.load', 'yaml.load', (), '', False, 'import yaml\n'), ((45, 41, 45, 44), 'pycbc.utils.AttrDict', 'd', ({}, {}), '()', True, 'from pycbc.utils import AttrDict as d\n'), ((47, 22, 47, 55), 'os.getenv', 'os.getenv', ({(47, 32, 47, 44): '"""ENV_PREFIX"""', (47, 46, 47, 54): '"""PYCBC_"""'}, {}), "('ENV_PREFIX', 'PYCBC_')", False, 'import os\n'), ((49, 21, 49, 65), 'os.getenv', 'os.getenv', ({(49, 31, 49, 55): 'f"""{env_prefix}S3_BUCKET"""', (49, 57, 49, 64): '"""pycbc"""'}, {}), "(f'{env_prefix}S3_BUCKET', 'pycbc')", False, 'import os\n'), ((52, 8, 52, 66), 'os.getenv', 'os.getenv', ({(52, 18, 52, 44): 'f"""{env_prefix}S3_FILENAME"""', (52, 46, 52, 65): '"""pycbc-config.yaml"""'}, {}), "(f'{env_prefix}S3_FILENAME', 'pycbc-config.yaml')", False, 'import os\n'), ((16, 19, 16, 40), 'cryptography.fernet.Fernet.generate_key', 'Fernet.generate_key', ({}, {}), '()', False, 'from cryptography.fernet import Fernet\n'), ((57, 12, 57, 31), 'copy.deepcopy', 'deepcopy', ({(57, 21, 57, 30): '_DEFAULTS'}, {}), '(_DEFAULTS)', False, 'from copy import deepcopy\n'), ((22, 23, 24, 14), 'pycbc.utils.AttrDict', 'd', ({(22, 25, 24, 13): "{'format': '%(asctime)-15s - %(levelname)-7s - %(message)s'}"}, {}), "({'format': '%(asctime)-15s - %(levelname)-7s - %(message)s'})", True, 'from pycbc.utils import AttrDict as d\n'), ((27, 23, 32, 14), 'pycbc.utils.AttrDict', 'd', ({(27, 25, 32, 13): "{'class': 'logging.StreamHandler', 'formatter': 'default', 'level': 'DEBUG',\n    'stream': 'ext://sys.stderr'}"}, {}), "({'class': 'logging.StreamHandler', 'formatter': 'default', 'level':\n    'DEBUG', 'stream': 'ext://sys.stderr'})", True, 'from pycbc.utils import AttrDict as d\n'), ((35, 21, 38, 14), 'pycbc.utils.AttrDict', 'd', ({(35, 23, 38, 13): "{'handlers': ['console'], 'level': 'INFO'}"}, {}), "({'handlers': ['console'], 'level': 'INFO'})", True, 'from pycbc.utils import AttrDict as d\n')]