import asyncio
import omni.kit.app
import omni.ext

from .async_unittest import AsyncTestCase
from .async_unittest import AsyncTestCaseFailOnLogError
from .async_unittest import AsyncTestSuite
from .utils import get_setting, get_global_test_output_path, get_test_output_path
from .ext_utils import decompose_test_list
from .ext_utils import extension_from_test_name
from .ext_utils import find_disabled_tests
from .ext_utils import get_module_to_extension_map
from .ext_utils import test_only_extension_dependencies
from . import unittests
from .unittests import get_tests_from_modules
from .unittests import get_tests_to_remove_from_modules
from .unittests import run_tests
from .unittests import get_tests
from .unittests import remove_from_dynamic_test_cache
from .exttests import run_ext_tests, shutdown_ext_tests
from .exttests import ExtTest, ExtTestResult
from .test_reporters import TestRunStatus
from .test_reporters import add_test_status_report_cb
from .test_reporters import remove_test_status_report_cb
from .test_coverage import PyCoverageCollector
from .test_populators import DEFAULT_POPULATOR_NAME, TestPopulator, TestPopulateAll, TestPopulateDisabled
from .reporter import generate_report

try:
    from omni.kit.omni_test_registry import omni_test_registry
except ImportError:
    # omni_test_registry is copied at build time into the omni.kit.test extension directory in _build
    pass


async def _auto_run_tests(run_tests_and_exit: bool):
    # Skip 2 updates to make sure all extensions loaded and initialized
    await omni.kit.app.get_app().next_update_async()
    await omni.kit.app.get_app().next_update_async()

    # Run Extension tests?
    # This part runs on the parent Kit Process that triggers all extension tests
    test_exts = list(get_setting("/exts/omni.kit.test/testExts", default=[]))
    if len(test_exts) > 0:
        # Quit on finish:
        def on_finish(result: bool):
            # generate coverage report at the end?
            if get_setting("/exts/omni.kit.test/testExtGenerateCoverageReport", default=False):
                generate_report()
            returncode = 0 if result else 21
            omni.kit.app.get_app().post_quit(returncode)

        exclude_exts = list(get_setting("/exts/omni.kit.test/excludeExts", default=[]))
        run_ext_tests(test_exts, on_finish_fn=on_finish, exclude_exts=exclude_exts)
        return

    # Print tests?
    # This part runs on the child Kit Process to print the number of extension tests
    if len(test_exts) == 0 and get_setting("/exts/omni.kit.test/printTestsAndQuit", default=False):
        unittests.print_tests()
        omni.kit.app.get_app().post_quit(0)
        return

    # Run python tests?
    # This part runs on the child Kit Process that performs the extension tests
    if run_tests_and_exit:
        tests_filter = get_setting("/exts/omni.kit.test/runTestsFilter", default="")

        from unittest.result import TestResult

        # Quit on finish:
        def on_finish(result: TestResult):
            returncode = 0 if result.wasSuccessful() else 13
            cpp_test_res = get_setting("/exts/omni.kit.test/~cppTestResult", default=None)
            if cpp_test_res is not None:
                returncode += cpp_test_res
            if not get_setting("/exts/omni.kit.test/doNotQuit", default=False):
                omni.kit.app.get_app().post_quit(returncode)

        unittests.run_tests(unittests.get_tests(tests_filter), on_finish)


class _TestAutoRunner(omni.ext.IExt):
    """Automatically run tests based on setting"""

    def __init__(self):
        super().__init__()
        self._py_coverage = PyCoverageCollector()

    def on_startup(self):
        # Report generate mode?
        if get_setting("/exts/omni.kit.test/testExtGenerateReport", default=False):
            generate_report()
            omni.kit.app.get_app().post_quit(0)
            return

        # Otherwise: regular test run
        run_tests_and_exit = get_setting("/exts/omni.kit.test/runTestsAndQuit", default=False)
        ui_mode = get_setting("/exts/omni.kit.test/testExtUIMode", default=False)

        # If launching a Python test then start test coverage subsystem (might do nothing depending on the settings)
        if run_tests_and_exit or ui_mode:
            self._py_coverage.startup()

        def on_app_ready(e):
            asyncio.ensure_future(_auto_run_tests(run_tests_and_exit))

        self._app_ready_sub = (
            omni.kit.app.get_app()
            .get_startup_event_stream()
            .create_subscription_to_pop_by_type(
                omni.kit.app.EVENT_APP_READY, on_app_ready, name="omni.kit.test start tests"
            )
        )

    def on_shutdown(self):
        # Stop coverage and generate report if it's started.
        self._py_coverage.shutdown()

        shutdown_ext_tests()
