#!/usr/bin/env python
from contextlib import contextmanager
from optparse import OptionParser
import logging
import sys
import json
import tempfile
import os
import signal

from load.projects.tankapi_cmd.src.client import LunaparkTank, TestPerformer


YAML = '.yaml'
INI = '.ini'


# required for non-tty python runs
def signal_handler(signal, frame):
    raise KeyboardInterrupt()


def add_tmp_file(source_file):
    _, ext = os.path.splitext(source_file)
    fd, filename = tempfile.mkstemp(ext, "load_")
    logging.info('tmp file: {}'.format(filename))
    with open(source_file) as orig:
        os.write(fd, orig.read().encode(encoding='UTF-8'))
    os.close(fd)
    return filename


@contextmanager
def tmp_files(source_files):
    tmps = [add_tmp_file(f) for f in source_files]
    try:
        yield tmps
    finally:
        [os.remove(tmp_file) for tmp_file in tmps]


def main():
    signal.signal(signal.SIGINT, signal_handler)

    # parse cmdline
    parser = OptionParser()
    parser.add_option('-a', '--ammo', action='store', help="Ammo file")
    parser.add_option('-c',
                      '--config',
                      action='append',
                      help="Test config file, multiple files accepted")
    parser.add_option(
        '-d',
        '--download',
        action='append',
        help="Download artifact file after test to current"
        " directory, filename masks supported, multiple options accepted")
    parser.add_option('-f',
                      '--file',
                      action='append',
                      help="Additional file, multiple options accepted")
    parser.add_option(
        '-j',
        '--jobno-file',
        dest='jobno',
        action='store',
        help="Save tank test number and Lunapark job number into file")
    parser.add_option('-m',
                      '--multi-tank',
                      action='store_true',
                      help="Start distributed test using all tanks")
    parser.add_option('-o',
                      '--option',
                      action='append',
                      help="Append section.option=value to config file")
    parser.add_option('-s',
                      '--status-file',
                      dest='status',
                      action='store',
                      help="Save tank status data into file")
    parser.add_option('-t',
                      '--tank',
                      action='append',
                      help="Tank API address, multiple options accepted")
    parser.add_option('-p',
                      '--port',
                      action='store',
                      help="Tank API port",
                      default="8083")
    parser.add_option('-v',
                      '--verbose',
                      action='store_true',
                      help="Show debug messages")
    parser.add_option('-C',
                      '--patch-cfg',
                      action='append',
                      help='Additional piece(s) of config. YAML format. Duplicated options will be overwritten.')
    parser.add_option('-T',
                      '--timeout',
                      type="int",
                      dest='timeout')
    options, ammofile = parser.parse_args()

    # setup logging
    logger = logging.getLogger('')
    logger.setLevel(logging.DEBUG)
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(logging.DEBUG if options.verbose else
                             logging.INFO)
    console_handler.setFormatter(logging.Formatter(
        "%(asctime)s %(levelname)s: %(message)s", "%H:%M:%S"))
    logger.addHandler(console_handler)

    # check options
    if not options.tank:
        raise ValueError("Tank address required")

    # create client
    clients = []
    for tank in options.tank:
        logging.info("Creating API client: %s:%s", tank, options.port)
        clients.append(LunaparkTank(tank, options.port, options.timeout))

    # do work
    if options.status:
        logging.info("Requesting tank status: %s", clients[0].host)
        res = clients[0].get_status()
        if not res['success']:
            raise RuntimeError(res['error'])
        logging.info("Saving status to %s", options.status)
        handle = open(options.status, 'w')
        handle.write(json.dumps(res, sort_keys=True, indent=4))
        handle.close()

    if options.config:
        performer = TestPerformer()

        if options.ammo:
            ammo = options.ammo
        else:
            ammo = None

        performer.choose_available_tank(clients, options.multi_tank)
        logging.info("Starting test with %s", options.config)

        if options.option:
            cli_options = [opt.strip('\'"') for opt in options.option]
        else:
            cli_options = []
        if options.patch_cfg:
            cfg_patches = options.patch_cfg
        else:
            cfg_patches = []

        with tmp_files(options.config) as cfg_files:
            rc = performer.perform(cfg_files, ammo, options.file, options.jobno,
                                   options.download, cli_options, cfg_patches)

        logging.info("RC: %s", rc)

        if rc is None:
            raise RuntimeError("RC cannot be None! Such a fail...")
        exit(rc)


if __name__ == "__main__":
    main()
