"""
Tasks for maintaining the project.

Execute 'invoke --list' for guidance on using Invoke
"""
import platform
import shutil
import webbrowser
from pathlib import Path

from invoke import task  # type: ignore
from invoke.runners import Failure, Result  # type: ignore

ROOT_DIR = Path(__file__).parent
SETUP_FILE = ROOT_DIR.joinpath("setup.py")
TEST_DIR = ROOT_DIR.joinpath("tests")
SOURCE_DIR = ROOT_DIR.joinpath("pytest_resource_path")
SETUP_PY = ROOT_DIR.joinpath("setup.py")
TASKS_PY = ROOT_DIR.joinpath("tasks.py")
COVERAGE_FILE = ROOT_DIR.joinpath(".coverage")
COVERAGE_DIR = ROOT_DIR.joinpath("htmlcov")
COVERAGE_REPORT = COVERAGE_DIR.joinpath("index.html")
PYTHON_DIRS = [str(d) for d in [SETUP_PY, TASKS_PY, SOURCE_DIR, TEST_DIR]]


def _delete_file(file):
    try:
        file.unlink(missing_ok=True)
    except TypeError:
        # missing_ok argument added in 3.8
        try:
            file.unlink()
        except FileNotFoundError:
            pass


@task(help={"check": "Checks if source is formatted without applying changes"})
def style(context, check=False):
    """
    Format code
    """
    for result in [isort(context, check), pipenv_setup(context, check), black(context, check)]:
        if result.failed:
            raise Failure(result)


def isort(context, check=False) -> Result:
    """Runs isort."""
    isort_options = "{}".format("--check-only --diff" if check else "")
    return context.run("isort {} {}".format(isort_options, " ".join(PYTHON_DIRS)), warn=True)


def pipenv_setup(context, check=False) -> Result:
    """Runs pipenv-setup."""
    isort_options = "{}".format("check --strict" if check else "sync --pipfile")
    return context.run("pipenv-setup {}".format(isort_options), warn=True)


def black(context, check=False) -> Result:
    """Runs black."""
    black_options = "{}".format("--check --diff" if check else "")
    return context.run("black {} {}".format(black_options, " ".join(PYTHON_DIRS)), warn=True)


@task
def lint_flake8(context):
    """
    Lint code with flake8
    """
    context.run("flake8 {} {}".format("--statistics --show-source --radon-show-closures", " ".join(PYTHON_DIRS)))


@task
def lint_pylint(context):
    """
    Lint code with pylint
    """
    context.run("pylint {}".format(" ".join(PYTHON_DIRS)))


@task
def lint_mypy(context):
    """
    Lint code with pylint
    """
    context.run("mypy {}".format(" ".join(PYTHON_DIRS)))


@task(lint_flake8, lint_pylint, lint_mypy)
def lint(_context):
    """
    Run all linting
    """


@task
def radon_cc(context):
    """
    Reports code complexity.
    """
    context.run("radon cc {}".format(" ".join(PYTHON_DIRS)))


@task
def radon_mi(context):
    """
    Reports maintainability index.
    """
    context.run("radon mi {}".format(" ".join(PYTHON_DIRS)))


@task(radon_cc, radon_mi)
def radon(_context):
    """
    Reports radon.
    """


@task
def xenon(context):
    """
    Check code complexity.
    """
    context.run(("xenon" " --max-absolute A" "--max-modules A" "--max-average A" "{}").format(" ".join(PYTHON_DIRS)))


@task
def test(context):
    """
    Run tests
    """
    pty = platform.system() == "Linux"
    context.run("python {} test".format(SETUP_FILE), pty=pty)


@task(help={"publish": "Publish the result via coveralls", "xml": "Export report as xml format"})
def coverage(context, publish=False, xml=False):
    """
    Create coverage report
    """
    context.run("coverage run --source {} -m pytest".format(SOURCE_DIR))
    context.run("coverage report")
    if publish:
        # Publish the results via coveralls
        context.run("coveralls")
        return
    # Build a local report
    if xml:
        context.run("coverage xml")
    else:
        context.run("coverage html")
        webbrowser.open(COVERAGE_REPORT.as_uri())


@task
def clean_build(context):
    """
    Clean up files from package building
    """
    context.run("rm -fr build/")
    context.run("rm -fr dist/")
    context.run("rm -fr .eggs/")
    context.run("find . -name '*.egg-info' -exec rm -fr {} +")
    context.run("find . -name '*.egg' -exec rm -f {} +")


@task
def clean_python(context):
    """
    Clean up python file artifacts
    """
    context.run("find . -name '*.pyc' -exec rm -f {} +")
    context.run("find . -name '*.pyo' -exec rm -f {} +")
    context.run("find . -name '*~' -exec rm -f {} +")
    context.run("find . -name '__pycache__' -exec rm -fr {} +")


@task
def clean_tests(_context):
    """
    Clean up files from testing
    """
    _delete_file(COVERAGE_FILE)
    shutil.rmtree(COVERAGE_DIR, ignore_errors=True)


@task(pre=[clean_build, clean_python, clean_tests])
def clean(_context):
    """
    Runs all clean sub-tasks
    """
