"""
Check code quality
"""
import os
import re

from paver.easy import BuildFailure, cmdopts, sh, task

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
REPORTS_DIR = os.path.join(ROOT_DIR, 'reports')

PACKAGES = [
    'VEDA',
    'VEDA_OS01',
    'control',
    'frontend',
    'youtube_callback',
    'scripts',
    'bin',
    'pavelib',
]


@task
@cmdopts([
    ("limit=", "l", "limit for number of acceptable violations"),
])
def run_pep8(options):
    """
    Run pep8 on system code.
    Fail the task if any violations are found.
    """
    violations_limit = int(getattr(options, 'limit', -1))

    sh('pep8 . | tee {report_dir}/pep8.report'.format(report_dir=REPORTS_DIR))

    num_violations, __ = _count_pep8_violations(
        '{report_dir}/pep8.report'.format(report_dir=REPORTS_DIR)
    )

    violations_message = violation_message('pep8', violations_limit, num_violations)
    print violations_message

    # Fail if number of violations is greater than the limit
    if num_violations > violations_limit > -1:
        raise BuildFailure(violations_message)


def _count_pep8_violations(report_file):
    """
    Returns a tuple of (num_violations, violations_list) for all
    pep8 violations in the given report_file.
    """
    with open(report_file) as f:
        violations_list = f.readlines()

    num_lines = len(violations_list)
    return num_lines, violations_list


@task
@cmdopts([
    ('errors', 'e', 'Check for errors only'),
    ('limit=', 'l', 'limit for number of acceptable violations'),
])
def run_pylint(options):
    """
    Run pylint on system code. When violations limit is passed in,
    fail the task if too many violations are found.
    """
    num_violations = 0
    violations_limit = int(getattr(options, 'limit', -1))
    errors = getattr(options, 'errors', False)

    flags = []
    if errors:
        flags.append('--errors-only')

    sh(
        'PYTHONPATH={python_path} pylint {packages} {flags} --msg-template={msg_template} | '
        'tee {report_dir}/pylint.report'.format(
            python_path=ROOT_DIR,
            packages=' '.join(PACKAGES),
            flags=' '.join(flags),
            msg_template='"{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}"',
            report_dir=REPORTS_DIR
        )
    )

    num_violations = _count_pylint_violations(
        '{report_dir}/pylint.report'.format(report_dir=REPORTS_DIR)
    )
    violations_message = violation_message('pylint', violations_limit, num_violations)
    print violations_message

    # Fail if number of violations is greater than the limit
    if num_violations > violations_limit > -1:
        raise BuildFailure(violations_message)


def _count_pylint_violations(report_file):
    """
    Parses a pylint report line-by-line and determines the number of violations reported
    """
    num_violations_report = 0
    # An example string:
    # scripts/reencode_crawler.py:57: [C0303(trailing-whitespace), ] Trailing whitespace
    pylint_pattern = re.compile(r'.(\d+):\ \[(\D\d+.+\]).')

    for line in open(report_file):
        violation_list_for_line = pylint_pattern.split(line)
        # If the string is parsed into four parts, then we've found a violation. Example of split parts:
        # test file, line number, violation name, violation details
        if len(violation_list_for_line) == 4:
            num_violations_report += 1

    return num_violations_report


def violation_message(task_name, violations_limit, num_violations):
    """
    Returns violation message for a task.

    Arguments:
        task_name (str): name of task
        violations_limit (int): total number of violations allowed
        num_violations (int): violations occurred
    """
    return '{violations_base_message}{violations_limit_message}'.format(
        violations_base_message='Too many {} violations. Number of {} violations: {}. '.format(
            task_name, task_name, num_violations
        ),
        violations_limit_message='The limit is {violations_limit}.'.format(violations_limit=violations_limit),
    )