from __future__ import annotations

import os
from typing import TYPE_CHECKING

import ulauncher.api
from ulauncher.api._logging import get_extension_logger
from ulauncher.api.event import EventType
from ulauncher.gi import GLib
from ulauncher.utils.socket_msg_controller import SocketMsgController, summarize_ipc_args

if TYPE_CHECKING:
    from ulauncher.internals import ipc


class Client:
    """
    Manages the extension's communication with Ulauncher.

    The socket connection is established before this process starts:
    - Ulauncher (parent process) creates a socket pair (two connected endpoints)
    - Parent keeps one endpoint, passes the other's FD to child process (this)

    Communication layers:
    → Extension subclass
    • This class
    → SocketMsgController
    → (OS) Unix socket (pair) connection
    → Ulauncher ExtensionRuntime (parent runtime)
    """

    extension: ulauncher.api.Extension
    msg_controller: SocketMsgController
    mainloop: GLib.MainLoop

    def __init__(self, extension: ulauncher.api.Extension) -> None:
        fd_str = os.environ.get("SOCKETPAIR_FD")
        if not fd_str:
            err_msg = "SOCKETPAIR_FD environment variable is required"
            raise RuntimeError(err_msg)
        try:
            file_descriptor = int(fd_str)
        except (TypeError, ValueError) as exc:
            err_msg = "SOCKETPAIR_FD must be a valid integer"
            raise RuntimeError(err_msg) from exc

        self.extension = extension
        self.logger = get_extension_logger()
        self.mainloop = GLib.MainLoop()
        self.msg_controller = SocketMsgController(file_descriptor, on_close=self.unload)

    def connect(self) -> None:
        """
        Sets up message listener and starts the GLib mainloop (blocks thread).
        """
        self.msg_controller.listen(self.on_message)

        self.logger.debug("Starting GLib mainloop")
        self.mainloop.run()
        self.logger.debug("GLib mainloop stopped")

    def on_message(self, message: ipc.EventEnvelope) -> None:
        """
        Parses message from Ulauncher and triggers extension event
        """
        self.logger.debug("Incoming message: %s", summarize_ipc_args([message]))
        try:
            event, request_id = message
        except (TypeError, ValueError):
            self.logger.warning("Ignoring malformed message: %s", summarize_ipc_args([message]))
            return
        self.extension.trigger_event(event, request_id)

    def unload(self) -> None:
        # trigger unload event and release mainloop so that the process will exit after the event is handled
        self.extension.trigger_event({"type": EventType.UNLOAD})
        if self.mainloop.is_running():
            self.mainloop.quit()

    def send(self, message: ipc.ExtensionMessage, log: bool = True) -> None:
        """Send a message to the app"""
        if log:
            self.logger.debug("Send message %s", summarize_ipc_args([message]))
        self.msg_controller.send(message)
