#! /opt/local/bin/python
# -*- coding: utf-8 -*-

# PyNX - Python tools for Nano-structures Crystallography
#   (c) 2018-present : ESRF-European Synchrotron Radiation Facility
#       authors:
#         Vincent Favre-Nicolin, favre@esrf.fr

import sys
import os
import io
import unittest
import warnings
import argparse
from pynx.version import get_git_version
from pynx.test import *


def make_parser():
    """Make parser for pynx-test command-line script"""
    parser = argparse.ArgumentParser(prog="pynx-test",
                                     description="Run the PyNX test suite\n",
                                     epilog="Examples:\n"
                                            "* pynx-test: run all tests\n"
                                            "* pynx-test --opencl: run all tests, only using opencl (no cuda)\n"
                                            "* pynx-test processing_unit --cuda: run only the "
                                            "processing_unit testsuite, only using cuda\n"
                                            "* pynx-test cdi: run the CDI API testsuite\n"
                                            "* pynx-test ptycho ptycho_runner: run the ptycho and "
                                            "ptycho runners testsuite\n"
                                     ,
                                     formatter_class=argparse.RawTextHelpFormatter)

    parser.add_argument("suite", nargs='*', default=None,
                        choices=['cdi',
                                 'cdi_runner',
                                 'holotomo',
                                 'imports',
                                 'processing_unit',
                                 'ptycho',
                                 'ptycho_runner',
                                 'scattering',
                                 'wavefront',
                                 []  # for python<3.12 https://bugs.python.org/issue27227
                                 ],
                        help="name of the specific testsuite which can be run. "
                             "By default all will be executed.")

    grp = parser.add_mutually_exclusive_group()
    grp.add_argument("--opencl", action='store_true',
                     help="Only run OpenCL tests (no cuda)")
    grp.add_argument("--cuda", action='store_true',
                     help="Only run CUDA tests (no OpenCL)")

    grp = parser.add_mutually_exclusive_group()
    grp.add_argument("--mailto_fail", action='store', type=str,
                     help="Will mail test results to given address, using localhost, only if tests fail")
    grp.add_argument("--mailto", action='store', type=str,
                     help="Will mail test results to given address, using localhost")

    parser.add_argument("--smtp", action='store', type=str,
                        default='localhost', help="SMTP server hostname server for mailto")

    parser.add_argument("--liveplot", "--live_plot", action='store_true',
                        help="Include live plotting tests")

    parser.add_argument("--oldparse", action='store_true',
                        help=argparse.SUPPRESS)
    return parser


def run_tests():
    test_suite = unittest.TestSuite()
    mailto = None
    mail_no_error = True

    execs = os.path.split(sys.argv[0])[-1] + ' '
    cmd = execs + ' '.join(sys.argv[1:])

    # Fix the input arguments for old style command-line parameters
    warn = False
    argv = [sys.argv[0]]
    for i in range(1, len(sys.argv)):
        if sys.argv[i] in ['opencl', 'cuda', 'liveplot', 'oldparse'] or \
                ('mailto' in sys.argv[i] and '--mailto' not in sys.argv[i]):
            argv.append('--' + sys.argv[i])
            warn = True
        else:
            if len(sys.argv[i].strip()):  # weird but ' ' can occur in argv...
                argv.append(sys.argv[i])

    if warn:
        warn_argparse = \
            "###########################################################################\n" \
            "DEPRECATION WARNING:\n" \
            "  You are still using the old-style arguments, " \
            "so your command-line was converted from: \n\n" \
            f"   {cmd}\n\n" \
            "To:\n\n" \
            f"  {execs + ' '.join(argv[1:])}\n\n" \
            "Please switch to the new format, as the old won't be supported " \
            "in PyNX versions released >=2025.\n\n" \
            f"Use '{execs}--help' for the command-line help \n" \
            "###########################################################################"
        print(warn_argparse)

    params = vars(make_parser().parse_args(argv[1:]))

    if params['mailto_fail'] is not None:
        mailto = params['mailto']
        mail_no_error = False
        print("Will mail results to %s if there are errors" % mailto)
    elif params['mailto'] is not None:
        mailto = params['mailto']
        print("Will mail results to %s" % mailto)

    for s in params['suite']:
        try:
            test_suite.addTests(eval(f"{s}_suite()"))
        except:
            print(f"Could not find the following unittest suite: {s}_suite")

    if test_suite.countTestCases() == 0:
        # No specific suite was specified from the command line, so use the general one
        test_suite = suite()

    res = unittest.TextTestRunner(verbosity=2, descriptions=False).run(test_suite)

    info = f"Running:\n{cmd}\n\n"

    nb_err_fail = len(res.errors) + len(res.failures)
    if len(res.errors):
        info += "\nERRORS:\n\n"
        for t, s in res.errors:
            tid = t.id()
            tid1 = tid.split('.')[-1]
            tid0 = tid.split('.' + tid1)[0]
            info += '%s (%s):\n' % (tid1, tid0) + s
    if len(res.failures):
        info += "\nFAILURES:\n\n"
        for t, s in res.failures:
            tid = t.id()
            tid1 = tid.split('.')[-1]
            tid0 = tid.split('.' + tid1)[0]
            info += '%s (%s):\n\n' % (tid1, tid0) + s + '\n\n'

    if mailto is not None and ((nb_err_fail > 0) or mail_no_error):
        import smtplib
        from email.message import EmailMessage

        msg = EmailMessage()
        msg['From'] = mailto
        msg['to'] = mailto
        msg['Subject'] = 'PyNX test results [nb_fail=%d nb_error=%d version=%s]' % \
                         (len(res.failures), len(res.errors), get_git_version())

        msg.set_content(info)

        s = smtplib.SMTP(params['smtp'])
        s.send_message(msg)
        s.quit()
        print("Sent message with subject: %s" % msg['Subject'])

    sys.exit(int(nb_err_fail > 0))


def main():
    with warnings.catch_warnings():
        warnings.simplefilter('ignore')
        run_tests()


if __name__ == '__main__':
    main()
