# components/i2c/i2c_logger_ui.py
#
from PyQt6.QtWidgets import (
    QWidget,
    QVBoxLayout,
    QHBoxLayout,
    QPushButton,
    QComboBox,
    QLineEdit,
    QSplitter,
)
from PyQt6.QtCore import QObject, pyqtSignal
from PyQt6.QtCore import Qt


from components.console.console_ui import console_widget
from components.console.console_registry import log_main_console
import components.items.elements as elements
import config.config as config

from components.i2c.i2c_logic import I2CLogic, _coerce_int


class _SafeConsoleProxy(QObject):
    log_signal = pyqtSignal(str, str)  # level, message

    def __init__(self, console):
        super().__init__()
        self.log_signal.connect(console.log)

    def __call__(self, level, msg):
        self.log_signal.emit(level, msg)


class I2CLoggerWidget(QWidget):
    """Drop-in single-page I2C widget with table | console splitter."""

    def __init__(self, parent=None):
        super().__init__(parent)
        self.i2c_logic = I2CLogic()
        self.commands = self.i2c_logic.get_predefined_commands()
        self.comboboxes = {}
        self.connection_status = False
        self.init_ui()

    def init_ui(self):
        # Top controls
        top_controls = QWidget()
        top_controls_layout = QHBoxLayout(top_controls)
        top_controls_layout.setContentsMargins(0, 0, 0, 0)
        top_controls_layout.setSpacing(12)
        top_controls_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
        # Port
        self.comboboxes["port"] = QComboBox()
        self.comboboxes["port"].addItems(self.i2c_logic.get_channels())
        top_controls_layout.addWidget(
            elements.label_and_widget("Port", self.comboboxes["port"])
        )

        self.button_refresh_channels = elements.create_icon_button(
            config.REFRESH_BUTTON_ICON_LINK, icon_size=30, border_size=4
        )
        top_controls_layout.addWidget(self.button_refresh_channels)

        # Address (empty until connected → scan)
        self.comboboxes["address"] = QComboBox()
        top_controls_layout.addWidget(
            elements.label_and_widget("Address", self.comboboxes["address"])
        )

        self.button_refresh_address = elements.create_icon_button(
            config.REFRESH_BUTTON_ICON_LINK, icon_size=30, border_size=4
        )
        top_controls_layout.addWidget(self.button_refresh_address)

        self.input_register = QLineEdit()
        self.input_register.setPlaceholderText("0x04")
        top_controls_layout.addWidget(
            elements.label_and_widget("Register", self.input_register)
        )

        self.input_length = QLineEdit()
        self.input_length.setPlaceholderText("1")
        top_controls_layout.addWidget(
            elements.label_and_widget("Length", self.input_length)
        )

        # Set Zero / Connect / start / Disconnect
        self.button_set_zero = QPushButton("Set Zero")
        top_controls_layout.addWidget(
            elements.label_and_widget("", self.button_set_zero)
        )

        self.button_connect = QPushButton("Connect")
        top_controls_layout.addWidget(
            elements.label_and_widget("", self.button_connect)
        )

        self.button_start = QPushButton("Start")
        top_controls_layout.addWidget(elements.label_and_widget("", self.button_start))

        self.button_disconnect = QPushButton("Disconnect")
        top_controls_layout.addWidget(
            elements.label_and_widget("", self.button_disconnect)
        )

        col1_widget = QWidget()
        col1_layout = QVBoxLayout(col1_widget)
        # col1_layout.setContentsMargins(0, 0, 0, 0)
        col1_layout.setSpacing(4)

        # Console
        self.console = console_widget()

        # IMPORTANT: keep a strong reference so it isn’t GC’d
        self._i2c_console_proxy = _SafeConsoleProxy(self.console)
        self.i2c_logic.set_logger(self._i2c_console_proxy)

        # === Console only ===
        console_stack_widget = QWidget()
        console_stack_layout = QVBoxLayout(console_stack_widget)
        console_stack_layout.setContentsMargins(0, 0, 0, 0)
        console_stack_layout.setSpacing(4)
        console_stack_layout.addWidget(self.console)

        # === Splitter (kept for consistency, only console pane) ===
        splitter = QSplitter(Qt.Orientation.Horizontal)
        splitter.addWidget(console_stack_widget)
        splitter.setStretchFactor(0, 1)

        # === Main Layout ===
        main_layout = QVBoxLayout()
        main_layout.setContentsMargins(4, 4, 4, 4)
        main_layout.setSpacing(6)
        main_layout.addWidget(top_controls)
        main_layout.addWidget(splitter, stretch=1)
        self.setLayout(main_layout)

        # Signals
        self.button_refresh_channels.clicked.connect(self.refresh_channels)
        self.button_refresh_address.clicked.connect(self.refresh_address)
        self.button_set_zero.clicked.connect(self.set_zero)
        self.button_connect.clicked.connect(self.connect)
        self.button_start.clicked.connect(self.start_logging)
        self.button_disconnect.clicked.connect(self.disconnect)

        self.disconnected_enable_status()

    # --- UI state toggles ---
    def disconnected_enable_status(self):
        elements.set_enabled_state(True, self.button_refresh_channels, grayOut=False)
        elements.set_enabled_state(True, self.comboboxes["port"], grayOut=False)
        elements.set_enabled_state(False, self.button_disconnect, grayOut=True)
        elements.set_enabled_state(True, self.button_connect, grayOut=False)
        elements.set_enabled_state(False, self.button_refresh_address, grayOut=True)
        elements.set_enabled_state(False, self.comboboxes["address"], grayOut=True)
        elements.set_enabled_state(False, self.input_register, grayOut=True)
        elements.set_enabled_state(False, self.button_start, grayOut=True)
        elements.set_enabled_state(False, self.input_length, grayOut=True)

    def connected_enable_status(self):
        elements.set_enabled_state(False, self.button_refresh_channels, grayOut=True)
        elements.set_enabled_state(False, self.comboboxes["port"], grayOut=True)
        elements.set_enabled_state(True, self.button_disconnect, grayOut=False)
        elements.set_enabled_state(False, self.button_connect, grayOut=True)
        elements.set_enabled_state(True, self.button_refresh_address, grayOut=False)
        elements.set_enabled_state(True, self.comboboxes["address"], grayOut=False)
        elements.set_enabled_state(True, self.input_register, grayOut=False)
        elements.set_enabled_state(True, self.button_start, grayOut=False)
        elements.set_enabled_state(True, self.input_length, grayOut=False)

    def started_enable_status(self):
        elements.set_enabled_state(False, self.button_refresh_channels, grayOut=True)
        elements.set_enabled_state(False, self.comboboxes["port"], grayOut=True)
        elements.set_enabled_state(True, self.button_disconnect, grayOut=False)
        elements.set_enabled_state(False, self.button_connect, grayOut=True)
        elements.set_enabled_state(False, self.button_refresh_address, grayOut=True)
        elements.set_enabled_state(False, self.comboboxes["address"], grayOut=True)
        elements.set_enabled_state(False, self.input_register, grayOut=True)
        elements.set_enabled_state(False, self.button_start, grayOut=True)
        elements.set_enabled_state(False, self.input_length, grayOut=True)

    # --- Buttons ---
    def set_zero(self):
        log_main_console("info", "Stting Zero")
        self.i2c_logic.measure_zero_raw14()

    def connect(self):
        log_main_console("info", "🔗 Connecting...")
        success = self.i2c_logic.connect(self.comboboxes["port"].currentText())
        if success:
            self.connected_enable_status()
            self.refresh_address(silent=True)
            self.connection_status = True
        else:
            elements.flash_button(
                self.button_connect, flash_style="background-color: red;"
            )

    def start_logging(self):
        # use the combobox address and user fields (reg, length)
        addr = self._current_address_int()
        reg_txt = (self.input_register.text() or "").strip()
        len_txt = (self.input_length.text() or "").strip() or "1"

        def _to_int_any(s, default=None):
            try:
                return int(str(s), 0)
            except Exception:
                return default

        reg = _to_int_any(reg_txt)
        length = _to_int_any(len_txt, default=1)
        if reg is None or length is None or length <= 0:
            self.console.log("warning", "⚠️ Invalid register or length")
            return

        # start the logic-side logger (read-only continuous)
        if self.i2c_logic.start_logger(
            device_address=addr, reg=reg, length=length, interval_ms=100
        ):
            self.started_enable_status()
        else:
            elements.flash_button(
                self.button_start, flash_style="background-color: red;"
            )

    def disconnect(self):
        log_main_console("info", "🔌 Disconnecting...")
        if self.i2c_logic.disconnect():
            self.disconnected_enable_status()
            self.connection_status = False
            self.refresh_channels(silent=True)
        else:
            elements.flash_button(
                self.button_disconnect, flash_style="background-color: red;"
            )

    def refresh_channels(self, silent: bool = False):
        log_main_console("info", "🔄 Refreshing buses...")
        self.comboboxes["port"].clear()
        ports = self.i2c_logic.get_channels()
        if ports:
            self.comboboxes["port"].addItems(ports)
            if not silent:
                elements.flash_button(self.button_refresh_channels)
            log_main_console("success", "🔄 Bus list refreshed")
        else:
            elements.flash_button(
                self.button_refresh_channels, flash_style="background-color: red;"
            )
            log_main_console("warn", "No I2C buses found")

    def refresh_address(self, silent: bool = False):
        log_main_console("info", "🔄 Scanning bus for devices...")
        self.comboboxes["address"].clear()
        addresses = self.i2c_logic.scan_bus()
        if addresses:
            self.comboboxes["address"].addItems(addresses)
            if not silent:
                elements.flash_button(self.button_refresh_address)
            log_main_console("success", "🔄 Bus scan complete")
        else:
            elements.flash_button(
                self.button_refresh_address, flash_style="background-color: red;"
            )
            log_main_console("info", "No devices detected")

    def get_current_config(self):
        return {key: cb.currentText() for key, cb in self.comboboxes.items()}

    def _current_address_int(self) -> int:
        txt = self.comboboxes["address"].currentText().strip() or "0x40"
        return _coerce_int(txt)

    def send_command(self, command: dict):
        self.i2c_logic.send_command(command, device_address=self._current_address_int())

    def send_command_raw(self):
        action = self.comboboxes["action"].currentText().strip().upper()
        reg_txt = (self.input_register.text() or "").strip()
        data_txt = (self.input_hex.text() or "").strip()
        length_txt = (self.input_length.text() or "").strip()

        # --- helpers ---
        def _to_int_any(s, default=None):
            try:
                return int(str(s), 0)  # accepts "0xFE", "254"
            except Exception:
                return default

        def _split_tokens(s: str):
            return [t for t in s.replace(",", " ").split() if t]

        # --- validate register ---
        reg = _to_int_any(reg_txt)
        if reg is None:
            self.console.log("warning", "⚠️ Invalid or missing register (e.g. 0xFE).")
            return

        cmd = {
            "action": action.lower(),
            "reg": reg,
        }

        if action == "READ":
            ln = _to_int_any(length_txt, default=1)
            if ln is None or ln <= 0:
                self.console.log("warning", "⚠️ Invalid length (must be >0).")
                return
            cmd["length"] = ln

        elif action == "WRITE":
            toks = _split_tokens(data_txt)
            if not toks:
                self.console.log(
                    "warning", "⚠️ WRITE requires data (e.g. 00 or 01,02,03)."
                )
                return
            if len(toks) == 1:
                v = _to_int_any(toks[0])
                if v is None:
                    self.console.log(
                        "warning", "⚠️ Invalid byte. Use hex/dec like 0x00 or 0."
                    )
                    return
                cmd["value"] = v
            else:
                cmd["bytes"] = data_txt  # leave as string; logic will parse

        self.i2c_logic.send_command(cmd, device_address=self._current_address_int())
