import os
import json
import shutil

from contextlib import contextmanager

import click
import daemon

from erroremail import ErrorEmail

from dadd import client
from dadd.worker import app
from dadd.worker.child_process import create_env, ProcessEnv
from dadd.worker.processes.python import PythonWorkerProcess
from dadd.master.utils import update_config
from dadd.runner.logger import get_logger


def printf(msg, fh):
    fh.write(msg + '\n')
    fh.flush()


@contextmanager
def foreground_context():
    yield


class ErrorHandler(object):
    def __init__(self, spec, logfile, sess=None):
        self.spec = spec
        self.logfile = logfile
        self.conn = client.connect(app, sess)
        self.log = get_logger(logfile)

    def upload_log(self):
        if not os.path.exists(self.logfile):
            return

        with open(self.logfile, 'r') as logfile:
            pid = client.get_pid(self.spec)
            if pid:
                client.set_proc_logfile(self.conn, pid, logfile)
            else:
                self.log.info('No process_id in spec. Here is the log.')
                for line in logfile:
                    self.log.info(line)

    def send_error_email(self, *args):
        if 'ERROR_EMAIL_CONFIG' in app.config:
            mailer = ErrorEmail(app.config['ERROR_EMAIL_CONFIG'])
            message = mailer.create_message_from_traceback(args)
            mailer.send_email(message)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        pid = client.get_pid(self.spec)

        if None in args:
            # client.set_process_state(self.conn, pid, 'success')
            return True

        self.upload_log()
        client.set_process_state(self.conn, pid, 'failed')
        self.send_error_email(*args)
        return True


def find_env(spec, working_dir):
    # Create a directory to work under and specify our log file.
    if working_dir:
        env = ProcessEnv(
            spec, working_dir,
            os.path.join(os.path.abspath(working_dir), 'output.log')
        )
        return env

    env = create_env(spec)
    print('Created Working Directory: %s' % env.directory)
    return env


def configure_environ(spec):
    # Add our spec as an APP_SETTINGS_JSON now that we are daemonized.
    config_filename = os.path.abspath('./specfile.json')
    with open(config_filename, 'w+') as fh:
        json.dump(spec, fh)
    os.environ['APP_SETTINGS_JSON'] = config_filename


@click.command()
@click.argument('specfile', type=click.File())
@click.option('--no-cleanup', is_flag=True, default=False)
@click.option('--foreground', is_flag=True, default=False)
@click.option('--working-dir', '-w')
def runner(specfile, no_cleanup, foreground, working_dir=None):
    # Make sure our config is up to date with our parent
    update_config(app)
    app.logger.info('update config before daemonizing')

    # Load our spec file
    spec = json.load(specfile)
    app.logger.info('loading the spec file')

    env = find_env(spec, working_dir)

    if not foreground:
        daemon_context = daemon.DaemonContext(
            detach_process=True,
            working_directory=working_dir
        )

        # Make sure we give our worker a chance to do clean up and notify
        # the master on any errors.
        # daemon_context.signal_map.update({
        #     signal.SIGTERM: worker.cleanup,
        # })
    else:
        app.logger.info('Running in the foreground')
        daemon_context = foreground_context()

    with daemon_context:

        with ErrorHandler(spec, env.logfile) as error_handler:
            configure_environ(spec)

            with open(env.logfile, 'w+') as output:
                worker = PythonWorkerProcess(spec, output)

                printf('Setting up', output)
                worker.setup()
                printf('Starting', output)
                try:
                    worker.start()
                except:
                    import traceback
                    printf(traceback.format_exc())
                    error_handler.upload_log()
                    raise

                printf('Finishing', output)
                worker.finish()
                printf('Done')

        # NOTE: These log messages essentiall fall into the ether b/c
        #       we are daemonized had to close our log file before
        #       uploading it...
        if no_cleanup:
            app.logger.info('Not cleaning up %s' % working_dir)
        else:
            app.logger.info('Cleaning up the working directory: %s' % working_dir)
            shutil.rmtree(working_dir)

@@ -2466,16 +2466,139 @@
 ename%0A%0A%0A
+def run_in_foreground(foreground):%0A    if app.config.get('RUNNER_FOREGROUND'):%0A        return True%0A    return foreground%0A%0A%0A
 @click.c
@@ -3157,20 +3157,34 @@
 %0A    if 
-not 
+run_in_foreground(
 foregrou
@@ -3185,16 +3185,17 @@
 reground
+)
 :%0A      
@@ -3188,37 +3188,140 @@
 round):%0A        
-daemo
+app.logger.info('Running in the foreground')%0A        run_context = foreground_context()%0A    else:%0A        ru
 n_context = daem
@@ -3429,24 +3429,30 @@
 )%0A%0A        #
+ TODO:
  Make sure w
@@ -3492,16 +3492,32 @@
 clean up
+%0A        #      
  and not
@@ -3519,26 +3519,16 @@
 d notify
-%0A        #
  the mas
@@ -3556,21 +3556,18 @@
       # 
-daemo
+ru
 n_contex
@@ -3651,132 +3651,20 @@
  %7D)%0A
-    else:%0A        app.logger.info('Running in the foreground')%0A        daemon_context = foreground_context()%0A%0A    with daemo
+%0A    with ru
 n_co

Allow configuring the runner to run apps in the foreground.