#!/usr/bin/env python
# -*-mode: python; indent-tabs-mode: nil; py-indent-offset: 4; coding: utf-8 -*-

"""
Runs doctests locallly
doctest files are in the tests/ directory.

Note that when writing new test files, it will be convenient to use the command-line flags to avoid time-consuming reprovisioning or to target particular boxes or tests.
"""

from __future__ import print_function
from sys import stderr

import argparse
import doctest
import glob
import re
import subprocess
import sys
import os

OPTIONS = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE

# Convenience items for testing.
# We'll pass these as globals to the doctests.

if os.path.exists('/dev/null'):
    DEV_NULL = open('/dev/null', 'w')
    EXE='vagrant'
else:
    DEV_NULL = open('NUL:', 'w')
    EXE='sh /i/bin/vagrant.msys'

# find all our available boxes
#with open('Vagrantfile', 'r') as f:
#    avail_boxes = re.findall(r'^\s+config.vm.define "(.+?)"', f.read(), re.MULTILINE)
# unused because it could be a Ruby variable

parser = argparse.ArgumentParser(description='Run playbook tests.')
parser.add_argument(
    '-f', '--force',
    action='store_true',
    help="Force tests to proceed if box already exists. Do not destroy box at end of tests."
    )
parser.add_argument(
    '-n', '--no-provision',
    action='store_true',
    help="Skip provisioning."
    )
parser.add_argument(
    '-F', '--fail-fast',
    action='store_true',
    help="REPORT_ONLY_FIRST_FAILURE."
    )
parser.add_argument(
    '-o', '--options',
    help=""
    )
parser.add_argument(
    '--haltonfail',
    action='store_true',
    help="Stop multibox tests after a fail; leave box running."
    )
parser.add_argument(
    '--file',
    help="Specify a single doctest file (default tests/*.txt).",
    )
parser.add_argument(
    '--box',
    help="Specify a particular target box",
    action="append",
    )

args = parser.parse_args()
if args.box:
    lBoxes = args.box
else:
    # find all our available running boxes
    # sed -e 's/ .*//'
    try:
        s = os.system("vagrant global-status 2>&1| grep running | cut -f 1 -d ' ' ")
    except StandardError as e:
        print("ERROR: Unable to find any running  boxes. Rerun with the --box argument.", file=sys.stderr)
        raise
    assert s, "ERROR: Unable to find a running box. Rerun with the --box argument."
    lBoxes = s.split(' ')

# mplatform = None
# def get_mplatform():
#     global mplatform
#     # Linux-4.14.80-gentoo-x86_64-Intel-R-_Pentium-R-_CPU_N3700_@_1.60GHz-with-gentoo-2.2.1
#     if mplatform is None:
#         mplatform = subprocess.check_output(
#             """vagrant ssh %s -c 'python -mplatform'""" % box,
#             shell=True,
#             stderr=DEV_NULL
#             )
#     return mplatform

print (repr(args))

def ssh_run(cmd):
    """
        Run a command line in a vagrant box via vagrant ssh.
        Return the output.
    """

    return subprocess.check_output(
        """%s ssh %s -c '%s'""" % (EXE, box, cmd),
        shell=True,
        stderr=DEV_NULL
        ).replace('^@', '')


def run(cmd):
    """
        Run a command in the host.
        Stop the tests with a useful message if it fails.
    """

    if sys.platform.startswith('win'):
        p = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            )
    else:
        p = subprocess.Popen(
            cmd,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            close_fds=True
            )
    stdout, stderr = p.communicate()
    if p.returncode != 0:
        print(stdout, file=sys.stderr)
        # Stop the doctest
        raise KeyboardInterrupt(stderr)
    return stdout

def cut(y, column_nums, sort=False):
    """
        returns a list of lines reduced to the chosen column_nums
    """
    assert y and len(y) > 0, "Empty string passed to cut"
    #
    if hasattr(y,'encode'):
        s = y.encode('utf-8')
    else:
        s = y

    lines = s.splitlines()
    line_lists = [l.split() for l in lines if l]
    rez = ["\t".join([col[col_num]
                      for col_num in column_nums if col_num < len(col)])
           for col in line_lists]
    if sort:
        return sorted(rez)
    else:
        return rez


def joined_cut(s, column_nums, sort=False):
    return "\n".join(cut(s, column_nums, sort))


for box in lBoxes:
    globs = {
        'ssh_run': ssh_run,
        'run': run,
        'cut': cut,
        'joined_cut': joined_cut,
        'skip_provisioning': args.no_provision,
        'no_provisioning': args.no_provision,
        'forcing': args.force,
        'box': box,
        }

    if args.fail_fast:
        OPTIONS = doctest.REPORT_ONLY_FIRST_FAILURE | OPTIONS
    if box and not args.force:
        output = subprocess.check_output("%s status %s" % (EXE, box,), shell=True)
        if re.search(r"%s\s+not created" % box, output) is None:
            print( "Vagrant box already exists. Destroy it or use '-f' to skip this test.", file=sys.stderr)
            print ("Use '-f' in combination with '-n' to skip provisioning.", file=sys.stderr)
            exit(1)

    if args.file is None:
        files = glob.glob('tests/*.txt')
    else:
        files = [args.file]

    for fn in files:
        print ( "%s / %s" % (box, fn) , file=sys.stderr)

        print( '*' * 50 )
        print (box)
        print( '*' * 50 )
        print (fn)
        print( '*' * 50 )
        try:
            failure_count, test_count = doctest.testfile(fn,
                                                        module_relative=False,
                                                        optionflags=OPTIONS,
                                                        globs=globs)
        except Exception as e:
            sys.stderr.write('\n'.join(sys.path) +'\n')
            raise
        if args.haltonfail and failure_count > 0:
            print ("Test failures occurred. Stopping tests and leaving vagrant box %s running." % box , file=sys.stderr)
            exit(1)

        # Clean up our vagrant box.

        if box and not args.force:
            print ( "Destroying %s" % box , file=sys.stderr)
            run("%s destroy %s -f" % (EXE, box,))
        elif box:
            print ( "Vagrant box %s left running." % box, file=sys.stderr)

