#! /usr/bin/env python

import sys
import os
import os.path
from subprocess import Popen
import traceback
from optparse import OptionParser
import tempfile
import shutil
import json
import logging

from generate_settings import simulated_install
from validation_results import SETTINGS_VALIDATION_ERROR_RC, SUCCESS_RC
from __init__ import run_installed_tests_as_subprocess


logger = logging.getLogger()

verbose = False

class TestException(Exception):
    pass


def system(command):
    """Run a command in the shell. If the command fails, we thow an
    exception.
    """
    p = Popen(command, shell=True)
    (pid, exit_status) = os.waitpid(p.pid, 0)
    rc = exit_status >> 8 # the low byte is the signal ending the proc
    if rc != 0:
        raise TestException("Command execution failed: '%s'" % command)


PACKAGE_MAIN=os.path.abspath(os.path.dirname(__file__))
PYTHON_EXE=sys.executable

def run_packager(args, expected_rc=0):
    """Run the packager in a subprocess. If the command does not exit with the
       expected return code, we thow an exception.
    """
    if verbose:
        command = "%s %s -v %s" % (PYTHON_EXE, PACKAGE_MAIN, " ".join(args))
    else:
        command = "%s %s %s" % (PYTHON_EXE, PACKAGE_MAIN, " ".join(args))
    p = Popen(command, shell=True)
    (pid, exit_status) = os.waitpid(p.pid, 0)
    rc = exit_status >> 8 # the low byte is the signal ending the proc
    if rc != expected_rc:
        raise TestException("Packager execution failed: '%s', rc was %s, expecting %s" % (command, rc, expected_rc))


def _run_validation_tests(archive_file, app_module_name, tmpdir):
        run_packager(["validate-safe", archive_file])
        run_packager(["validate-full", archive_file])
        # for the validate-installed case, we need to simulate the installation
        (install_path, undo_ops) = simulated_install(archive_file, tmpdir,
                                                     app_module_name)
        results_file = os.path.join(tmpdir, "results.json")
        run_packager(["validate-installed", install_path, results_file])
        assert os.path.exists(results_file)
        fileobj = open(results_file, "rb")
        json_obj = json.load(fileobj)
        fileobj.close()
        if type(json_obj)!=dict:
            raise TestException("Invalid results file, contents are %s" %
                                json_obj.__repr__())
        os.remove(results_file)
        parsed_results = run_installed_tests_as_subprocess(install_path,
                                                           app_module_name,
                                                           use_logger=logger)
        if parsed_results.get_return_code()!=SUCCESS_RC:
            if parsed_results.has_error_messages():
                logger.debug("parsed results bad, messages = %s" %
                             parsed_results.format_messages())
            raise TestException("run as subprocess test failed, rc was %d" %
                                parsed_results.get_return_code())

    
def test_directory(dir_path, app_module_name, options):
    tmpdir = tempfile.mkdtemp(suffix="-engage-test")
    try:
        archive_file = os.path.join(tmpdir, "test_archive.tgz")
        if options.prepare_negative_test:
            run_packager(["prepare-dir", dir_path, app_module_name, archive_file],
                         expected_rc=SETTINGS_VALIDATION_ERROR_RC)
            logger.info("Negative tests for prepare-dir successful.")
        else:
            run_packager(["prepare-dir", dir_path, app_module_name, archive_file])
            _run_validation_tests(archive_file, app_module_name, tmpdir)
            logger.info("Tests for prepare-dir successful.")
        return 0
    finally:
        shutil.rmtree(tmpdir)


def test_archive(archive_file, app_module_name, options):
    tmpdir = tempfile.mkdtemp(suffix="-engage-test")
    try:
        if options.prepare_negative_test:
            run_packager(["prepare-archive", archive_file, app_module_name],
                         expected_rc=SETTINGS_VALIDATION_ERROR_RC)
            logger.info("Negative tests for prepare-archive successful.")
        else:
            run_packager(["prepare-archive", archive_file, app_module_name])
            _run_validation_tests(archive_file, app_module_name, tmpdir)
            logger.info("Tests for prepare-archive successful.")
        return 0
    finally:
        shutil.rmtree(tmpdir)



def main(argv):
    try:
        parser = OptionParser(usage="%prog [options] django_archive_file django_settings_module\n%prog [options] django_app_directory django_settings_module")
        parser.add_option("-n", "--prepare-negative-test", action="store_true",
                          dest="prepare_negative_test", default=False,
                          help="Prepare validation checks should fail")
        parser.add_option("-v", "--verbose", action="store_true",
                          dest="verbose", default=False,
                          help="Print debug output for packager")
        (options, args) = parser.parse_args()
        if len(args)!=2:
            parser.error("Expecting two arguments")
        global verbose
        verbose = options.verbose
        if verbose:
            LOG_LEVEL = logging.DEBUG
        else:
            LOG_LEVEL = logging.INFO

        logger.setLevel(LOG_LEVEL)
        handler = logging.StreamHandler(sys.stdout)
        handler.setLevel(LOG_LEVEL)
        logger.addHandler(handler)

        if os.path.isdir(args[0]):
            rc = test_directory(args[0], args[1], options)
        elif os.path.exists(args[0]):
            rc = test_archive(args[0], args[1], options)
        else:
            parser.error("File or directory %s does not exist" % args[0])
        return rc
    except TestException:
        (exc_type, exc_value, exc_traceback) = sys.exc_info()
        traceback.print_exception(exc_type, exc_value, None, file=sys.stderr)
        logger.error("Tests failed due to TestException")
        return 1
    except SystemExit, c:
        sys.exit(c)
    except:
        (exc_type, exc_value, exc_traceback) = sys.exc_info()
        traceback.print_exception(exc_type, exc_value, exc_traceback, file=sys.stderr)
        logger.error("Tests failed due to unexpected exception of type %s, value was %s" % (exc_type, exc_value))
        return 1


if __name__ == "__main__":
    sys.exit(main(sys.argv))
