import logging
import os
import sys
import time
from typing import List, Optional

import msgbus
from msgbus import MBContext, MBService, MBException
from local_component import BaseLocalProcess

logger = logging.getLogger(__name__)


class LocalHyperVisor(BaseLocalProcess):
    def __init__(
        self,
        address: str = "127.0.0.1",
        port: int = 16000,
        comp_dir: str = None,
        python_path: Optional[List[str]] = None,
        main_path: Optional[str] = None,
        debug_streams: bool = False,
        extra_env: Optional[dict] = None,
    ):
        super().__init__(
            component_name="HyperVisor",
            address=address,
            port=port,
            main_path=main_path or "main.py",
            comp_dir=comp_dir or os.path.dirname(os.path.abspath(__file__)),
            debug_streams=debug_streams,
        )
        self.python_path = python_path or []
        self.extra_env = extra_env or {}

    def _build_env(self) -> dict:
        env = {}
        if sys.platform == "win32":
            env["SystemRoot"] = os.environ["SystemRoot"]

        env.update({
            "LOADERCFG": "{}",
            "LOG_PATH": "{}",
            "HVIP": self.address,
            "HVPORT": str(self.port),
        })

        if self.python_path:
            env["PYTHONPATH"] = os.pathsep.join(
                os.path.join(self.comp_dir, folder) for folder in self.python_path
            )

        env.update(self.extra_env)
        return env

    def _wait_for_start(self):
        wait_process_running(
            comp_address=self.address,
            comp_port=self.port,
        )


def wait_process_running(comp_address: str, comp_port: int):
    mb_context = MBContext(
        my_service=None,
        name="CompWaiter"
    )

    try:
        other_service = MBService(
            address=comp_address,
            port=comp_port,
        )

        for _ in range(10):
            try:
                reply = mb_context.MB_SendMessage(
                    destination=other_service,
                    token=msgbus.TK_SYS_HEALTH_CHECK,
                )
                break
            except MBException:
                time.sleep(0.05)
        else:
            raise RuntimeError("Timeout waiting for component to start.")

        if reply.token != msgbus.TK_SYS_ACK:
            raise RuntimeError("Error on TK_SYS_HEALTH_CHECK")
    finally:
        mb_context.MB_Finalize()
