#!/usr/bin/env python
import os
import sys
import inspect
from functools import update_wrapper
import getpass
import copy

import click
import signals

from .config_reader import read_config
from .utils import version, dict_merge
from .defaults import DEFAULT_CONFIG


# add fixture feature to pocoo-click
def _make_command(f, name, attrs, cls):
    if isinstance(f, click.Command):
        raise TypeError('Attempted to convert a callback into a '
                        'command twice.')
    new_func = f
    if 'fixture' in attrs:
        fixture = attrs['fixture']
        if not inspect.isgeneratorfunction(fixture):
            raise TypeError('fixture does not yield anything.')
        attrs.pop('fixture')

        def new_func(*args, **kwargs):
            it = fixture()
            val = next(it)
            res = f(val, *args[1:], **kwargs)
            try:
                next(it)
            except StopIteration:
                pass
            else:
                raise RuntimeError('fixture has more than one yield.')
            return res

        try:
            new_func.__click_params__ = f.__click_params__
        except AttributeError:
            pass
        update_wrapper(new_func, f)

    try:
        params = new_func.__click_params__
        params.reverse()
        del new_func.__click_params__
    except AttributeError:
        params = []
    help = attrs.get('help')
    if help is None:
        help = inspect.getdoc(f)
        if isinstance(help, bytes):
            help = help.decode('utf-8')
    else:
        help = inspect.cleandoc(help)
    attrs['help'] = help
    click.decorators._check_for_unicode_literals()
    return cls(name=name or new_func.__name__.lower(),
               callback=new_func, params=params, **attrs)

# patch in the custom command maker
click.decorators._make_command = _make_command


def _get_env():
    """Read ENV environment variable.
    """
    env = os.getenv('ENV', '')
    if env:
        env = env.lower()
    return env


def _get_context():
    """assemble the tool context. Private members are preceded by a '_'.
    :return: dictionary containing the tool context
    """
    # TODO: elapsed, artifact
    click_xtc = click.get_current_context()
    context = {
        'tool': click_xtc.parent.info_name,
        'command': click_xtc.info_name,
        'version': version(),
        'user': getpass.getuser()
    }

    env = _get_env()
    if env:
        context['env'] = env

    return context


def lifecycle():
    
"""Tool lifecycle which provides hooks into the different stages of the
    command execution. See signals for hook details.
    """
    context = _get_context()
    signals.initialized.send(context)
    config, ok = read_config(context, DEFAULT_CONFIG)
    if not ok:
        signals.exit_on_error.send(context)
        sys.exit()

    # TODO credentials_retr

    # TODO check credentials are valid

    signals.config_validation_init.send((context, config))
    # TODO config validation
    signals.config_validation_finalized.send((context, config))

    # merge DEFAULT_CONFIG with config
    tool_config = copy.deepcopy(DEFAULT_CONFIG[context['tool']])
    dict_merge(tool_config, config[context['tool']])

    # TODO lookups

    # run the command and provide context and config
    signals.command_init.send((context, tool_config))
    yield context, tool_config
    signals.command_finalized.send((context, tool_config))

    signals.finalized.send(context)