#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys
import subprocess
import zmq
import ConfigParser
import logging

import threebot_crypto
from threebot_worker.daemon import Daemon

PROTOCOL = 'tcp'

BASE_FOLDERNAME = '3bot'
BASEDIR = os.path.join(os.path.expanduser("~"), BASE_FOLDERNAME)

if os.environ.get('VIRTUAL_ENV', False):  # in case worker runs in a virtualenv, set BASEDIR to path of virtualenv
    BASEDIR = os.path.join(os.path.realpath(os.environ['VIRTUAL_ENV']), BASE_FOLDERNAME)

CONFIGFILE = os.path.join(BASEDIR, 'config.ini')
PIDFILE = os.path.join(BASEDIR, '3bot-worker.pid')
SCRIPTDIR = os.path.join(BASEDIR, 'logs')

LOGFILE = os.path.join(BASEDIR, '3bot.log')
LOGLEVEL = 'ERROR'

Config = ConfigParser.ConfigParser()

if os.path.isfile(CONFIGFILE):
    Config.read(CONFIGFILE)
else:
    print "No configfile found in: '%s'" % CONFIGFILE
    print "----"
    print "Creating basic configfile in '%s'" % CONFIGFILE
    print "----"

    os.makedirs(BASEDIR)
    cfgfile = open(CONFIGFILE, 'w')

    # add the settings to the structure of the file, and lets write it out...
    Config.add_section('3bot-settings')
    Config.set('3bot-settings', 'BOT_ENDPOINT', '*')

    port = raw_input('Enter PORT: ')
    Config.set('3bot-settings', 'PORT', port)

    sec_key = raw_input('Enter SECRET_KEY: ')
    Config.set('3bot-settings', 'SECRET_KEY', sec_key)

    Config.write(cfgfile)
    cfgfile.close()

FLAGS = 0

try:
    BOT = Config.get('3bot-settings', 'BOT_ENDPOINT')
    PORT = Config.get('3bot-settings', 'PORT')
except:
    print "Invalid config file in: '%s'. Could not find BOT or PORT declaration" % CONFIGFILE
    print "You can find a basic config file in the documentation."
    sys.exit(2)

try:
    SECRET_KEY = Config.get('3bot-settings', 'SECRET_KEY')
except:
    print "Invalid config file in: '%s'. Could not find SECRET_KEY declaration" % CONFIGFILE
    print "You can find a basic config file in the documentation."
    sys.exit(2)


try:
    LOGFILE = Config.get('3bot-settings', 'LOGFILE')
except ConfigParser.NoOptionError:
    pass

try:
    LOGLEVEL = Config.get('3bot-settings', 'LOGLEVEL')
except ConfigParser.NoOptionError:
    pass

if LOGLEVEL == 'DEBUG':
    loglevel = logging.DEBUG
elif LOGLEVEL == 'INFO':
    loglevel = logging.INFO
elif LOGLEVEL == 'WARNING':
    loglevel = logging.WARNING
elif LOGLEVEL == 'ERROR':
    loglevel = logging.ERROR
else:
    LOGLEVEL = logging.CRITICAL

logging.basicConfig(
    filename=LOGFILE,
    level=loglevel,
    format='%(asctime)s %(message)s',
)

if len(sys.argv) == 2:
    if 'start' == sys.argv[1] or 'restart' == sys.argv[1]:
        print '---'
        print "Try Starting Worker with following settings found in '%s'" % CONFIGFILE
        print 'ENDPOINT: %s' % str(BOT)
        print 'PORT: %s' % str(PORT)
        print 'LOGFILE: %s' % str(LOGFILE)
        print 'LOGLEVEL: %s' % str(LOGLEVEL)
        print '---'


def write_script(directory, script, body):
    # create and change to log directory
    task_path = os.path.join(directory, script)

    # create file
    with open(task_path, 'w+') as task_file:
        task_file.write(body.encode('utf8').replace('\r\n', '\n'))
        logging.info("Saving new Script file at: %s" % task_path)

    # change permission
    os.chmod(task_path, 0755)

    return task_path


def run_command(request):
    """
    Calls the action
    """
    response = {}
    log_id = request['workflow_log_id']
    log_time = request['workflow_log_time']
    workflow_name = request['workflow']
    foldername = "%s-%s-%s" % (log_time, str(log_id), workflow_name)

    directory = os.path.join(SCRIPTDIR, foldername)

    if not os.path.exists(directory):
        os.makedirs(directory)

    script_bits = []

    # NOTE: keep order of hooks and task
    # TODO: cleaner implementation, not so verbose

    # add pre task hook if available
    if request.get('hooks', ) is not None:
        if request['hooks'].get('pre_task', ) is not None:
            task_filename = "pre_task_%i" % request['script']['id']
            script_bits.append(write_script(directory, task_filename, request['hooks']['pre_task']))

    # the main task
    task_filename = "script_%i" % request['script']['id']
    task_path = os.path.join(directory, task_filename)
    task_body = request['script']['body']
    script_bits.append(write_script(directory, task_filename, task_body))

    # add post task hook if available
    if request.get('hooks', ) is not None:
        if request['hooks'].get('post_task', ) is not None:
            task_filename = "post_task_%i" % request['script']['id']
            script_bits.append(write_script(directory, task_filename, request['hooks']['post_task']))

    callable = ''
    if len(script_bits) > 1:
        callable = " && ".join(script_bits)
    else:
        callable = script_bits[0]

    # execute task script
    # http://www.saltycrane.com/blog/2008/09/how-get-stdout-and-stderr-using-python-subprocess-module/
    p = subprocess.Popen(callable, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    logging.info("Executing Script file at: %s" % task_path)
    t_stdout = ""
    t_stderr = ""

    try:
        t_stdout = p.stdout.read()
    except AttributeError, e:
        logging.info(e)

    try:
        t_stderr = p.stderr.read()
    except AttributeError, e:
        logging.info(e)

    response = {'stdout': t_stdout, 'stderr': t_stderr, 'exit_code': p.wait()}
    del p

    return response


class WorkerDeamon(Daemon):

    def run(self):
        logging.info("Starting the 3bot worker listening on %s:%s" % (BOT, PORT))

        context = zmq.Context(1)
        server = context.socket(zmq.REP)
        server.bind("%s://%s:%s" % (PROTOCOL, BOT, PORT))

        while True:
            request = server.recv(FLAGS)
            request = threebot_crypto.decrypt(request, SECRET_KEY)
            logging.info("Received request")
            if request:
                response = {'type': 'NOOP'}
                if 'type' in request and request['type'] == 'ACC':
                    logging.info("ACK request")
                    response = {'type': 'ACK'}
                else:
                    logging.info("Script request")
                    response = run_command(request)
                response = threebot_crypto.encrypt(response, SECRET_KEY)
                server.send(response, flags=FLAGS)
                logging.info("Sending response")
            else:
                logging.error("Could not decrypt received message")
                if self.debug_mode:
                    raise Exception("Could not decrypt message")
            # server.send("", flags=FLAGS)


if __name__ == "__main__":
        if len(sys.argv) == 3:
            if 'start' == sys.argv[1] and 'debug' == sys.argv[2]:
                daemon = WorkerDeamon(PIDFILE, debug_mode=True)
                daemon.start()

        elif len(sys.argv) == 2:
            daemon = WorkerDeamon(PIDFILE)
            if 'start' == sys.argv[1]:
                daemon.start()
            elif 'stop' == sys.argv[1]:
                daemon.stop()
            elif 'restart' == sys.argv[1]:
                daemon.restart()
            elif 'status' == sys.argv[1]:
                daemon.status()
            else:
                print "Unknown command"
                sys.exit(2)
            sys.exit(0)
        else:
            print "usage: %s start|stop|restart|status" % sys.argv[0]
            sys.exit(2)
