"""
Unit test tasks
"""
import re
import os
import sys
from paver.easy import sh, task, cmdopts, needs
from pavelib.utils.test import suites
from pavelib.utils.envs import Env
from pavelib.utils.timer import timed
from pavelib.utils.passthrough_opts import PassthroughTask
from optparse import make_option

try:
    from pygments.console import colorize
except ImportError:
    colorize = lambda color, text: text

__test__ = False  # do not collect


@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("system=", "s", "System to act on"),
    ("test-id=", "t", "Test id"),
    ("fail-fast", "x", "Fail suite on first failed test"),
    ("fasttest", "a", "Run without collectstatic"),
    make_option(
        '-c', '--cov-args', default='',
        help='adds as args to coverage for the test run'
    ),
    ('skip-clean', 'C', 'skip cleaning repository before running tests'),
    ('processes=', 'p', 'number of processes to use running tests'),
    make_option('-r', '--randomize', action='store_true', help='run the tests in a random order'),
    make_option('--no-randomize', action='store_false', dest='randomize', help="don't run the tests in a random order"),
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
    make_option(
        '--disable-migrations',
        action='store_true',
        dest='disable_migrations',
        help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
    ),
    make_option(
        '--enable-migrations',
        action='store_false',
        dest='disable_migrations',
        help="Create tables by applying migrations."
    ),
    ("fail_fast", None, "deprecated in favor of fail-fast"),
    ("test_id=", None, "deprecated in favor of test-id"),
    ('cov_args=', None, 'deprecated in favor of cov-args'),
    make_option(
        "-e", "--extra_args", default="",
        help="deprecated, pass extra options directly in the paver commandline"
    ),
    ('skip_clean', None, 'deprecated in favor of skip-clean'),
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
@PassthroughTask
@timed
def test_system(options, passthrough_options):
    """
    Run tests on our djangoapps for lms and cms
    """
    system = getattr(options, 'system', None)
    test_id = getattr(options, 'test_id', None)

    if test_id:
        if not system:
            system = test_id.split('/')[0]
        if system in ['common', 'openedx']:
            system = 'lms'
        options.test_system['test_id'] = test_id

    if test_id or system:
        system_tests = [suites.SystemTestSuite(
            system,
            passthrough_options=passthrough_options,
            **options.test_system
        )]
    else:
        system_tests = []
        for syst in ('cms', 'lms'):
            system_tests.append(suites.SystemTestSuite(
                syst,
                passthrough_options=passthrough_options,
                **options.test_system
            ))

    test_suite = suites.PythonTestSuite(
        'python tests',
        subsuites=system_tests,
        passthrough_options=passthrough_options,
        **options.test_system
    )
    test_suite.run()


@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("lib=", "l", "lib to test"),
    ("test-id=", "t", "Test id"),
    ("failed", "f", "Run only failed tests"),
    ("fail-fast", "x", "Run only failed tests"),
    make_option(
        '-c', '--cov-args', default='',
        help='adds as args to coverage for the test run'
    ),
    ('skip-clean', 'C', 'skip cleaning repository before running tests'),
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
    ('cov_args=', None, 'deprecated in favor of cov-args'),
    make_option(
        '-e', '--extra_args', default='',
        help='deprecated, pass extra options directly in the paver commandline'
    ),
    ("fail_fast", None, "deprecated in favor of fail-fast"),
    ('skip_clean', None, 'deprecated in favor of skip-clean'),
    ("test_id=", None, "deprecated in favor of test-id"),
], share_with=['pavelib.utils.test.utils.clean_reports_dir'])
@PassthroughTask
@timed
def test_lib(options, passthrough_options):
    """
    Run tests for common/lib/ and pavelib/ (paver-tests)
    """
    lib = getattr(options, 'lib', None)
    test_id = getattr(options, 'test_id', lib)

    if test_id:
        if '/' in test_id:
            lib = '/'.join(test_id.split('/')[0:3])
        else:
            lib = 'common/lib/' + test_id.split('.')[0]
        options.test_lib['test_id'] = test_id
        lib_tests = [suites.LibTestSuite(
            lib,
            passthrough_options=passthrough_options,
            **options.test_lib
        )]
    else:
        lib_tests = [
            suites.LibTestSuite(
                d,
                passthrough_options=passthrough_options,
                **options.test_lib
            ) for d in Env.LIB_TEST_DIRS
        ]

    test_suite = suites.PythonTestSuite(
        'python tests',
        subsuites=lib_tests,
        passthrough_options=passthrough_options,
        **options.test_lib
    )
    test_suite.run()


@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("failed", "f", "Run only failed tests"),
    ("fail-fast", "x", "Run only failed tests"),
    make_option(
        '-c', '--cov-args', default='',
        help='adds as args to coverage for the test run'
    ),
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
    make_option(
        '--disable-migrations',
        action='store_true',
        dest='disable_migrations',
        help="Create tables directly from apps' models. Can also be used by exporting DISABLE_MIGRATIONS=1."
    ),
    ('cov_args=', None, 'deprecated in favor of cov-args'),
    make_option(
        '-e', '--extra_args', default='',
        help='deprecated, pass extra options directly in the paver commandline'
    ),
    ("fail_fast", None, "deprecated in favor of fail-fast"),
])
@PassthroughTask
@timed
def test_python(options, passthrough_options):
    """
    Run all python tests
    """
    python_suite = suites.PythonTestSuite(
        'Python Tests',
        passthrough_options=passthrough_options,
        **options.test_python
    )
    python_suite.run()


@needs(
    'pavelib.prereqs.install_prereqs',
    'pavelib.utils.test.utils.clean_reports_dir',
)
@cmdopts([
    ("suites", "s", "List of unit test suites to run. (js, lib, cms, lms)"),
    make_option(
        '-c', '--cov-args', default='',
        help='adds as args to coverage for the test run'
    ),
    make_option("--verbose", action="store_const", const=2, dest="verbosity"),
    make_option("-q", "--quiet", action="store_const", const=0, dest="verbosity"),
    make_option("-v", "--verbosity", action="count", dest="verbosity", default=1),
    ('cov_args=', None, 'deprecated in favor of cov-args'),
    make_option(
        '-e', '--extra_args', default='',
        help='deprecated, pass extra options directly in the paver commandline'
    ),
])
@PassthroughTask
@timed
def test(options, passthrough_options):
    """
    Run all tests
    """
    # Subsuites to be added to the main suite
    python_suite = suites.PythonTestSuite(
        'Python Tests',
        passthrough_options=passthrough_options,
        **options.test
    )
    js_suite = suites.JsTestSuite('JS Tests', mode='run', with_coverage=True)

    # Main suite to be run
    all_unittests_suite = suites.TestSuite('All Tests', subsuites=[js_suite, python_suite])
    all_unittests_suite.run()


@task
@needs('pavelib.prereqs.install_coverage_prereqs')
@cmdopts([
    ("compare-branch=", "b", "Branch to compare against, defaults to origin/master"),
    ("compare_branch=", None, "deprecated in favor of compare-branch"),
])
@timed
def coverage():
    """
    Build the html, xml, and diff coverage reports
    """
    report_dir = Env.REPORT_DIR
    rcfile = Env.PYTHON_COVERAGERC

    if not (report_dir / '.coverage').isfile():
        # This may be that the coverage files were generated using -p,
        # try to combine them to the one file that we need.
        sh("coverage combine --rcfile={}".format(rcfile))

    if not os.path.getsize(report_dir / '.coverage') > 50:
        # Check if the .coverage data file is larger than the base file,
        # because coverage combine will always at least make the "empty" data
        # file even when there isn't any data to be combined.
        err_msg = colorize(
            'red',
            "No coverage info found.  Run `paver test` before running "
            "`paver coverage`.\n"
        )
        sys.stderr.write(err_msg)
        return

    # Generate the coverage.py XML report
    sh("coverage xml --rcfile={}".format(rcfile))
    # Generate the coverage.py HTML report
    sh("coverage html --rcfile={}".format(rcfile))
    diff_coverage()  # pylint: disable=no-value-for-parameter


@task
@needs('pavelib.prereqs.install_coverage_prereqs')
@cmdopts([
    ("compare-branch=", "b", "Branch to compare against, defaults to origin/master"),
    ("compare_branch=", None, "deprecated in favor of compare-branch"),
], share_with=['coverage'])
@timed
def diff_coverage(options):
    """
    Build the diff coverage reports
    """
    compare_branch = options.get('compare_branch', 'origin/master')

    # Find all coverage XML files (both Python and JavaScript)
    xml_reports = []

    for filepath in Env.REPORT_DIR.walk():
        if bool(re.match(r'^coverage.*\.xml$', filepath.basename())):
            xml_reports.append(filepath)

    if not xml_reports:
        err_msg = colorize(
            'red',
            "No coverage info found.  Run `paver test` before running "
            "`paver coverage`.\n"
        )
        sys.stderr.write(err_msg)
    else:
        xml_report_str = ' '.join(xml_reports)
        diff_html_path = os.path.join(Env.REPORT_DIR, 'diff_coverage_combined.html')

        # Generate the diff coverage reports (HTML and console)
        sh(
            "diff-cover {xml_report_str} --compare-branch={compare_branch} "
            "--html-report {diff_html_path}".format(
                xml_report_str=xml_report_str,
                compare_branch=compare_branch,
                diff_html_path=diff_html_path,
            )
        )

        print "\n"
