import subprocess
import json
import uuid
import os
import time
import select
import ctypes
import msvcrt
# import win32api
# import win32con

class Daemon:
    def __init__(self, executable_path: str, log_path: str):
        self.executable_path = executable_path
        self.process = None
        self.log_path = log_path
        self.response_buffer = b""

    def start(self):
        self.process = subprocess.Popen(
            args=[self.executable_path, self.log_path],
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            bufsize=0,
        )

        # r_handle = msvcrt.get_osfhandle(self.process.stdout.fileno())
        # current_process = win32api.GetCurrentProcess()
        # self.read_handle = win32api.DuplicateHandle(
        #     current_process, r_handle,  # Source process & handle
        #     current_process, # Target process
        #     0, True,  # Inheritable=True
        #     win32con.DUPLICATE_SAME_ACCESS
        # )

    def send_command(self, command: str, params: list, timeout: float = 10.0) -> dict:
        """
        Send command and wait for response
        Returns: {"status": "success|error", ...}
        Raises TimeoutError if no response
        """
        cmd_id = str(uuid.uuid4())
        message = {
            "type": "command",
            "id": cmd_id,
            "data": {
                "name": command,
                "params": params
            }
        }
        
        # Send command
        data = json.dumps(message).encode("utf-8")
        header = len(data).to_bytes(4, "big")
        self.process.stdin.write(header + data)
        self.process.stdin.flush()
        
        # Wait for response
        handle = msvcrt.get_osfhandle(self.process.stdout.fileno())
        available = ctypes.c_ulong()
        start_time = time.time()

        while time.time() - start_time < timeout:
            # Check if process died
            if self.process.poll() is not None:
                raise BrokenPipeError("Daemon process died")
            
            # Try to read data
            ctypes.windll.kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(available), None)
            if not available.value > 0:
                time.sleep(0.1)
                continue

            self.response_buffer += self.process.stdout.read(available.value)
            
            # Try to parse messages from buffer
            if len(self.response_buffer) < 4:
                continue # Incomplete header

            length = int.from_bytes(self.response_buffer[:4], "big")
            if len(self.response_buffer) < 4 + length:
                continue  # Incomplete message
                
            # Extract full message
            msg_data = self.response_buffer[4:4+length]
            self.response_buffer = self.response_buffer[4+length:]
            
            message = json.loads(msg_data.decode("utf-8"), parse_float=float, parse_int=int)
            if message.get("id") == cmd_id:
                # check message for erros -- raise if we find em
                if message["status"] == "error":
                    raise RuntimeError(f"Daemon: {message['error']}")

                # no errors -- respond with data of the message
                return message["data"]

            # Unknown message response -- dump and keep waiting
            self.response_buffer = b""

        raise TimeoutError(f"Command {command} timed out!")
        
    def kill(self):
        if self.process:
            self.process.terminate()
            self.process.wait()
            self.process = None
