# -*- coding: utf-8 -*-

"""Console script for mystran_validation."""
import configparser
import glob
import logging
import os
import shlex
import shutil
import subprocess
import sys
import webbrowser
from pathlib import Path

import click
import pytest


def find(
    path,
    extensions,
    name="*",
    break_on_first=True,
    return_first=True,
    report_none="# path to file",
):
    """return a list of pathlib.Path canditates matching {name}{ext}"""
    if isinstance(path, str):
        path = Path(path)
    path = path  # .resolve()
    files = []
    for ext in extensions:
        pattern = str(path / f"{name}{ext}")
        _files = glob.glob(pattern)
        if _files:
            if break_on_first:
                files = _files
                break
            files += _files
    if not files:
        files = [report_none]
    files = [Path(f) for f in files]
    if return_first:
        return files[0]
    return files


def get_junit_files(rootdir):
    junit_file = rootdir / "mystran-testing.xml"
    junit_html_target = junit_file.parent / (junit_file.stem + ".html")
    return junit_file, junit_html_target


def setup(rootdir):
    # -------------------------------------------------------------------------
    # ensure conftest.py and __init__ is there
    _init = rootdir / "__init__.py"
    if not _init.exists():
        _init.touch()
    _conftest = rootdir / "conftest.py"
    if not _conftest.exists():
        with open(_conftest, "w") as fh:
            fh.write("from mystran_validation.conftest_ref import *")
    # delete junit stuff
    junit_file, junit_html_target = get_junit_files(rootdir)
    try:
        os.remove(junit_file)
    except FileNotFoundError:
        pass
    try:
        os.remove(junit_html_target)
    except FileNotFoundError:
        pass
    return


def teardown(rootdir):
    """clean rootdir"""
    to_delete = ["bandit.*", "conftest.py", "__init__.py", "__pycache__"]
    to_delete = [rootdir / p for p in to_delete]
    for pattern in to_delete:
        files = glob.glob(str(pattern))
        for file in files:
            try:
                shutil.rmtree(file)
            except NotADirectoryError:
                os.remove(file)


@click.group()
@click.option("--rootdir", default="~/mystran_test_cases")
@click.pass_context
def main(ctx, rootdir):
    rootdir = Path(rootdir).expanduser().resolve()
    setup(rootdir)
    click.echo(f"running tests in {str(rootdir)}")
    ctx.ensure_object(dict)
    ctx.obj["rootdir"] = rootdir


@main.command()
@click.argument("path", type=click.Path(exists=False))
@click.option("-f", "--force", help="force creation of path", is_flag=True)
@click.option("-v", "--verbose", help="print ini content", is_flag=True)
@click.pass_context
def dump_config(ctx, path, force, verbose):
    """dump configuration file as example"""
    # search for bulk file
    rootdir = ctx.obj["rootdir"]
    path = Path(rootdir).expanduser() / path
    if not path.exists():
        if force is True:
            path.mkdir(parents=True)
            logging.warning(f"creating {path}")
        else:
            logging.error(f"path {path} does not exist. Create it passing --force flag")
    # path = Path(path)#.resolve()
    bulkfile = find(path, extensions=(".nas", ".bdf", ".dat"))
    if not bulkfile:
        print(f"no bulk file found")
    # -------------------------------------------------------------------------
    # search for op2
    op2file = find(path, extensions=(".op2", ".OP2"))
    if not op2file:
        print(f"no op2 file found")
    # -------------------------------------------------------------------------
    # create test.ini
    config = configparser.ConfigParser()
    config["DEFAULT"] = {
        "title": path.stem,
        "bulk": bulkfile.relative_to(path),
        "reference": op2file.relative_to(path),
        "atol": 1e-5,
        "rtol": 1e-8,
    }
    config["Displacements"] = {
        "description": "checking all displacements",
        "vector": "displacements",
    }
    config["Reactions"] = {
        "description": "checking all reactions",
        "vector": "reactions",
    }
    target = path / f"test_{path.resolve().stem}.ini"
    with open(target, "w") as fh:
        config.write(fh)
    print(f"\nwrote {target}")
    if verbose:
        print("\n")
        with open(target, "r") as fh:
            for line in fh.read().split("\n"):
                print(line)
    return 0


@main.command(
    context_settings=dict(
        ignore_unknown_options=True,
    )
)
@click.option("--report/--no-report", default=True)
@click.option("--open-report/--not-open-report", default=True)
@click.argument("pytest_args", nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def run(ctx, report, open_report, pytest_args):
    rootdir = ctx.obj["rootdir"]

    args = list(pytest_args)
    if report:
        junit_file, junit_html_target = get_junit_files(rootdir)
        args += [f"--junitxml={junit_file}"]
    args.append(str(rootdir))
    pytest.main(args)
    if report:
        junitxml_bin = shutil.which("junit2html")
        subprocess.run(shlex.split(f"{junitxml_bin} {junit_file} {junit_html_target}"))
    teardown(rootdir)
    if report and open_report:
        webbrowser.open(str(junit_html_target.resolve()))
    return 0


@main.command(
    context_settings=dict(
        ignore_unknown_options=True,
    )
)
@click.argument("pytest_args", nargs=-1, type=click.UNPROCESSED)
@click.pass_context
def collect(ctx, pytest_args):
    rootdir = ctx.obj["rootdir"]
    args = list(pytest_args) + ["--collect-only"]
    args.append(str(rootdir))
    pytest.main(["--collect-only", str(rootdir)])
    teardown(rootdir)
    return 0


if __name__ == "__main__":
    sys.exit(main())  # pragma: no cover
