from pathlib import Path
from typing import Callable

from clarabot_nano.core.spawn.tight import TightlyControlledSpawn
from clarabot_nano.core.spawner.base import AbstractSpawner
from clarabot_nano.utility.subprocess.executable import EXEC_GUI


class TightPythonSpawner(AbstractSpawner):
    def __init__(
        self,
        pid_prefix,
        args,
        only_spawn_when_clean=False,
        spawn_callback: Callable[[TightlyControlledSpawn], None] = None,
        dispose_callback: Callable[[TightlyControlledSpawn], None] = None,
        number=1,
        gui_type=False,
    ):
        AbstractSpawner.__init__(
            self,
            instance_number=number,
            only_spawn_when_clean=only_spawn_when_clean,
            spawn_callback=spawn_callback,
            dispose_callback=dispose_callback,
        )
        self._gui_type = gui_type
        self._pid_prefix = pid_prefix
        self._args = args
        self._core_port = None
        self._core_auth_data = None

    def prepare(self, core_port: int, auth_data: bytes):
        self._core_port = core_port
        self._core_auth_data = auth_data

    def _spawn(self, index, nano_config):
        if self._core_port is None:
            raise Exception("Cannot spawn tightly coupled subprocess before preparing the spawner. (core_port is None)")
        spawn = self._create_spawn(
            index,
            nano_config.get_pid_dir(),
            self._pid_prefix,
            self._core_port,
            self._core_auth_data,
            # NOTE: EXEC_CONSOLE does not offer sufficient utilities on Windows, so we don't use it.
            #   They are the same on other platforms.
            [EXEC_GUI, "-m"] + self._args,
        )
        spawn.start()
        return spawn

    def _create_spawn(
        self, index: int, pid_dir: Path, pid_prefix: str, core_port: int, core_auth_data: bytes, popen_args
    ):
        return TightlyControlledSpawn(index, pid_dir, pid_prefix, core_port, core_auth_data, popen_args, self._gui_type)
