import os
import os.path
import sys

from e3.testsuite.driver.classic import TestSkip

from drivers.base_driver import BaseDriver


class PythonDriver(BaseDriver):

    py_file = 'test.py'

    def run(self):
        runner = PythonRunner(self)

        if not runner.is_python_api_available:
            raise TestSkip('Cannot test the Python API without shared'
                           ' libraries')
        if self.disable_python:
            raise TestSkip('Python API testing disabled')

        input_sources = self.test_env.get('input_sources', [])
        project_file = self.test_env.get('project_file', None)
        self.check_file(self.py_file)
        self.check_file_list('"input_sources"', input_sources)

        args = list(input_sources)

        # If there is a project file specified, add it to the arguments
        if project_file:
            self.check_file(project_file)
            args.append(f"-P{project_file}")

        runner.run(self.py_file, args)


class PythonRunner(object):
    """
    Helper to factorize facilities to run Python scripts using Libadalang.
    """

    def __init__(self, driver):
        self.driver = driver

    @property
    def is_python_api_available(self):
        """
        Return if Libadalang's Python API is available.
        """
        return not self.driver.disable_shared

    @staticmethod
    def add_paths(old_path, *dirnames):
        """
        Helper to build environment paths: prepend items in `dirnames` to
        `old_path`. This inserts `os.path.pathsep` where appropriate.

        :type old_path: str
        :type dirnames: list[str]
        :rtype: str
        """
        new_paths = os.path.pathsep.join(dirnames)
        return ('{}{}{}'.format(new_paths, os.path.pathsep, old_path)
                if old_path else new_paths)

    def run(self, py_file, py_args):
        """
        Run the given Python scripts with given arguments.
        """
        return self.driver.run_and_check(
            [self.interpreter, py_file] + py_args,
            env={
                'PYTHONPATH': self.add_paths(
                    os.environ.get('PYTHONPATH'),
                    self.support_dir,
                    self.internal_support_dir
                ),
                'LIBADALANG_ROOTDIR': os.path.join(
                    os.path.dirname(os.path.abspath(__file__)),
                    '..', '..',
                ),
                'LIBADALANG_DISABLE_SHARED': str(
                    int(self.driver.disable_shared)
                ),
                'LIBADALANG_BUILD_MODE': self.driver.build_mode,
            }
        )

    @property
    def interpreter(self):
        """
        Return the Python interpreter to use.
        """
        return (sys.executable
                if self.driver.use_testsuite_python else
                self.driver.python_interpreter)

    @property
    def support_dir(self):
        """
        Return the absolute path to the directory for support Python modules.
        """
        return os.path.join(self.driver.env.root_dir, 'python_support')

    @property
    def internal_support_dir(self):
        """
        Return the absolute path to the directory for support Python modules
        for the internal testsuite.
        """
        return os.path.join(self.driver.env.root_dir, 'tests', 'internal',
                            'python_support')
