from __future__ import annotations

from struct import pack, unpack
from pymodbus.client import ModbusTcpClient


def _f32_be(regs: list[int], idx: int) -> float:
    raw = pack(">HH", int(regs[idx]) & 0xFFFF, int(regs[idx + 1]) & 0xFFFF)
    return float(unpack(">f", raw)[0])


def _i64_be(regs: list[int], idx: int) -> int:
    raw = pack(
        ">HHHH",
        int(regs[idx]) & 0xFFFF,
        int(regs[idx + 1]) & 0xFFFF,
        int(regs[idx + 2]) & 0xFFFF,
        int(regs[idx + 3]) & 0xFFFF,
    )
    return int(unpack(">q", raw)[0])

def _u32_be(regs: list[int], idx: int) -> int:
    raw = pack(">HH", int(regs[idx + 1]) & 0xFFFF, int(regs[idx]) & 0xFFFF)
    return int(unpack(">i", raw)[0])


def _ascii_be(regs: list[int], idx: int, n_regs: int, *, strip_null: bool = True) -> str:
    b = bytearray()
    for r in regs[idx: idx + n_regs]:
        r &= 0xFFFF
        b.append((r >> 8) & 0xFF)   # high byte
        b.append(r & 0xFF)          # low byte

    if strip_null:
        b = b.split(b"\x00", 1)[0]  # stop at first NUL

    return b.decode("ascii", errors="replace").rstrip()

def _read_input_registers(client: ModbusTcpClient, *, address: int, count: int, unit_id: int):
    """
    pymodbus kwarg name varies by version: slave / unit / device_id / none.
    Keep it simple and just try the common ones.
    """
    fn = client.read_input_registers

    # Try keyword variations first
    for kw in ("slave", "unit", "device_id"):
        try:
            return fn(address=address, count=count, **{kw: unit_id})
        except TypeError:
            pass

    # Fallback: no unit parameter (some setups default to unit 1)
    return fn(address=address, count=count)


def read_master_loadguard(client: ModbusTcpClient, unit: int) -> dict:
    rr = _read_input_registers(client, address=2000, count=26, unit_id=unit)
    if rr.isError():
        raise RuntimeError(rr)

    r = rr.registers

    lg_raw = int(r[0])
    if lg_raw == 0:
        lg_status = "Not connected"
        lg_connected = False
    elif lg_raw == 1:
        lg_status = "Connected"
        lg_connected = True
    else:
        lg_status = "Unknown connection status!"
        lg_connected = None

    u_l1 = _f32_be(r, 2004 - 2000)
    u_l2 = _f32_be(r, 2006 - 2000)
    u_l3 = _f32_be(r, 2008 - 2000)

    i_l1 = _f32_be(r, 2010 - 2000)
    i_l2 = _f32_be(r, 2012 - 2000)
    i_l3 = _f32_be(r, 2014 - 2000)

    p_tot = _f32_be(r, 2022 - 2000)

    return {
        "lg_connected": lg_connected,
        "lg_status": lg_status,
        "u_l1_V": round(u_l1, 2),
        "u_l2_V": round(u_l2, 2),
        "u_l3_V": round(u_l3, 2),
        "i_l1_A": round(i_l1, 2),
        "i_l2_A": round(i_l2, 2),
        "i_l3_A": round(i_l3, 2),
        "p_total_kW": round(p_tot, 2),
    }
