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())

[((198, 216), 'boto3.client', 'boto3.client', (['"""s3"""'], {}), "('s3')\n", (210, 216), False, 'import boto3\n'), ((2208, 2247), 'yaml.load', 'yaml.load', (['data'], {'Loader': 'yaml.FullLoader'}), '(data, Loader=yaml.FullLoader)\n', (2217, 2247), False, 'import yaml\n'), ((1086, 1089), 'pycbc.utils.AttrDict', 'd', ([], {}), '()\n', (1087, 1089), True, 'from pycbc.utils import AttrDict as d\n'), ((1150, 1183), 'os.getenv', 'os.getenv', (['"""ENV_PREFIX"""', '"""PYCBC_"""'], {}), "('ENV_PREFIX', 'PYCBC_')\n", (1159, 1183), False, 'import os\n'), ((1242, 1286), 'os.getenv', 'os.getenv', (['f"""{env_prefix}S3_BUCKET"""', '"""pycbc"""'], {}), "(f'{env_prefix}S3_BUCKET', 'pycbc')\n", (1251, 1286), False, 'import os\n'), ((1357, 1415), 'os.getenv', 'os.getenv', (['f"""{env_prefix}S3_FILENAME"""', '"""pycbc-config.yaml"""'], {}), "(f'{env_prefix}S3_FILENAME', 'pycbc-config.yaml')\n", (1366, 1415), False, 'import os\n'), ((331, 352), 'cryptography.fernet.Fernet.generate_key', 'Fernet.generate_key', ([], {}), '()\n', (350, 352), False, 'from cryptography.fernet import Fernet\n'), ((1501, 1520), 'copy.deepcopy', 'deepcopy', (['_DEFAULTS'], {}), '(_DEFAULTS)\n', (1509, 1520), False, 'from copy import deepcopy\n'), ((511, 574), 'pycbc.utils.AttrDict', 'd', (["{'format': '%(asctime)-15s - %(levelname)-7s - %(message)s'}"], {}), "({'format': '%(asctime)-15s - %(levelname)-7s - %(message)s'})\n", (512, 574), True, 'from pycbc.utils import AttrDict as d\n'), ((666, 779), 'pycbc.utils.AttrDict', 'd', (["{'class': 'logging.StreamHandler', 'formatter': 'default', 'level': 'DEBUG',\n    'stream': 'ext://sys.stderr'}"], {}), "({'class': 'logging.StreamHandler', 'formatter': 'default', 'level':\n    'DEBUG', 'stream': 'ext://sys.stderr'})\n", (667, 779), True, 'from pycbc.utils import AttrDict as d\n'), ((912, 957), 'pycbc.utils.AttrDict', 'd', (["{'handlers': ['console'], 'level': 'INFO'}"], {}), "({'handlers': ['console'], 'level': 'INFO'})\n", (913, 957), True, 'from pycbc.utils import AttrDict as d\n')]