"""
workflow_check.py — System check workflow for ES24N IOM controllers.

Queries current network settings and firmware versions from one or both IOMs
via either a serial connection (for IOMs not yet on the network) or a direct
network connection (for IOMs already reachable via their management IP).
No changes are made — this is a read-only diagnostic workflow.
"""

import time

from redfish import _redfish_request, _get_iom_fw_version, _get_fabric_fw_version
from workflow_serial import (
    detect_serial_device,
    open_serial_connection,
    close_serial_connection,
    _login_serial_console,
    _serial_redfish_request,
)
from ui import (
    _c, C,
    banner, rule, draw_table,
    info, ok, warn, error,
    prompt, prompt_ip, prompt_password,
)


# ── Shared helpers ─────────────────────────────────────────────────────────────

def _sanitize(value: str) -> str:
    """Strip non-printable and non-ASCII characters from a string field.
    IOM Redfish responses occasionally contain binary artifacts in text fields."""
    return "".join(c for c in value if 32 <= ord(c) < 128)


def _parse_network_data(data: dict) -> tuple:
    """
    Extract network details from a Redfish EthernetInterfaces/1 response dict.
    Returns (dhcp_enabled, ip, gateway, netmask, mac, hostname, link_status).
    """
    dhcp_enabled = (
        data.get("DHCPv4", {}).get("DHCPEnabled", False) or
        data.get("DHCPv6", {}).get("DHCPEnabled", False)
    )

    # Prefer IPv4StaticAddresses; fall back to IPv4Addresses
    addrs = data.get("IPv4StaticAddresses") or data.get("IPv4Addresses", [])
    if addrs:
        addr    = addrs[0]
        ip      = _sanitize(addr.get("Address",    "--"))
        gateway = _sanitize(addr.get("Gateway",    "--"))
        netmask = _sanitize(addr.get("SubnetMask", "--"))
    else:
        ip = gateway = netmask = "--"

    mac         = _sanitize(data.get("MACAddress",  "--"))
    hostname    = _sanitize(data.get("HostName",    "--"))
    link_status = _sanitize(data.get("LinkStatus",  "--"))

    return dhcp_enabled, ip, gateway, netmask, mac, hostname, link_status


def _print_results(net_rows: list, iface_rows: list, fw_rows: list):
    """
    Display network settings, interface identity, and firmware versions.

    net_rows:   [IOM, Mode, IP Address, Gateway, Subnet Mask, Link Status]
    iface_rows: [IOM, MAC Address, Hostname]
    fw_rows:    [IOM, IOM Firmware, Fabric Firmware]
    """
    rule("Network Settings")
    draw_table(
        ["IOM", "Mode", "IP Address", "Gateway", "Subnet Mask", "Link"],
        net_rows,
        [5, 8, 15, 15, 15, 8],
    )
    print()

    rule("Interface Details")
    draw_table(
        ["IOM", "MAC Address",        "Hostname"],
        iface_rows,
        [5,     19,                   42],
    )
    print()

    rule("Firmware Versions")
    draw_table(
        ["IOM", "IOM Firmware", "Fabric Firmware"],
        fw_rows,
        [5, 32, 24],
    )
    print()


# ── Serial check ───────────────────────────────────────────────────────────────

def _check_via_serial():
    banner()
    rule("System Check -- Serial Connection")

    device = detect_serial_device()
    if not device:
        error("Could not detect a serial device. Returning to main menu.")
        time.sleep(2)
        return

    ser = open_serial_connection(device)
    if not ser:
        error("Could not open serial port. Returning to main menu.")
        time.sleep(2)
        return

    print()
    logged_in, password = _login_serial_console(ser)
    if not logged_in:
        error("Could not log in to IOM console. Returning to main menu.")
        close_serial_connection(ser, device)
        time.sleep(2)
        return

    # Prompt for which IOM the serial cable is connected to
    print()
    print("  Which IOM is the serial cable connected to?")
    print(f"    {_c(C.BOLD, '1')}  IOM1")
    print(f"    {_c(C.BOLD, '2')}  IOM2")
    print()
    while True:
        iom_choice = prompt("Select [1/2]")
        if iom_choice in ("1", "2"):
            break
        warn("Please enter 1 or 2.")
    iom = "IOM1" if iom_choice == "1" else "IOM2"

    rule(f"Querying {iom} Status")
    info(f"Querying {iom} network settings and firmware versions via serial console...")
    print()

    # ── Network settings ───────────────────────────────────────────────────────
    net_ok, net_data = _serial_redfish_request(
        ser, password, "GET",
        f"/redfish/v1/Managers/{iom}/EthernetInterfaces/1",
    )
    if net_ok and isinstance(net_data, dict):
        dhcp, ip, gw, nm, mac, hostname, link = _parse_network_data(net_data)
        mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
        net_rows   = [[iom, mode, ip, gw, nm, link]]
        iface_rows = [[iom, mac, hostname]]
    else:
        net_rows   = [[iom, _c(C.RED, "No response"), "--", "--", "--", "--"]]
        iface_rows = [[iom, "--", "--"]]
        error(f"{iom} network query failed: {net_data}")

    # ── IOM firmware version ───────────────────────────────────────────────────
    iom_ok, iom_data = _serial_redfish_request(
        ser, password, "GET",
        f"/redfish/v1/Managers/{iom}",
    )
    iom_ver = (
        iom_data.get("FirmwareVersion", "Unknown")
        if (iom_ok and isinstance(iom_data, dict))
        else _c(C.RED, "Unreachable")
    )

    # ── Fabric card firmware version ───────────────────────────────────────────
    fab_ok, fab_data = _serial_redfish_request(
        ser, password, "GET",
        f"/redfish/v1/Chassis/{iom}/NetworkAdapters/1",
    )
    fab_ver = (
        (fab_data.get("Oem", {})
                 .get("VikingEnterpriseSolutions", {})
                 .get("Version", {})
                 .get("ActiveFirmwareVersion", "Unknown"))
        if (fab_ok and isinstance(fab_data, dict))
        else _c(C.RED, "Unreachable")
    )

    _print_results(net_rows, iface_rows, [[iom, iom_ver, fab_ver]])
    close_serial_connection(ser, device)


# ── Network check ──────────────────────────────────────────────────────────────

def _check_via_network():
    banner()
    rule("System Check -- Network Connection")

    print()
    password = prompt_password()
    print()
    ip = prompt_ip("  IOM IP address (either IOM1 or IOM2)")

    iom_list = [("IOM1", ip), ("IOM2", ip)]

    rule("Querying IOM Status")
    info("Querying network settings and firmware versions over the network...")
    print()

    net_rows   = []
    iface_rows = []
    fw_rows    = []

    for iom, host in iom_list:
        # ── Network settings ───────────────────────────────────────────────────
        net_ok, net_data = _redfish_request(
            password, "GET",
            f"/redfish/v1/Managers/{iom}/EthernetInterfaces/1",
            host=host,
        )
        if net_ok and isinstance(net_data, dict):
            dhcp, ip_addr, gw, nm, mac, hostname, link = _parse_network_data(net_data)
            mode = _c(C.CYN, "DHCP") if dhcp else _c(C.GRN, "Static")
            net_rows.append([iom, mode, ip_addr, gw, nm, link])
            iface_rows.append([iom, mac, hostname])
        else:
            net_rows.append([iom, _c(C.RED, "No response"), "--", "--", "--", "--"])
            iface_rows.append([iom, "--", "--"])
            error(f"{iom} network query failed: {net_data}")

        # ── Firmware versions (reuse shared redfish helpers) ───────────────────
        iom_ver = _get_iom_fw_version(password, host, iom)
        fab_ver = _get_fabric_fw_version(password, host, iom)
        fw_rows.append([iom, iom_ver, fab_ver])

    _print_results(net_rows, iface_rows, fw_rows)
    prompt("Press Enter to return to main menu")


# ── Top-level entry point ──────────────────────────────────────────────────────

def system_check_workflow():
    banner()
    rule("System Check")

    print("  How do you want to connect to the IOM(s)?")
    print()
    print(f"    {_c(C.BOLD, '1')}  Serial connection   (IOM not yet on the network)")
    print(f"    {_c(C.BOLD, '2')}  Network connection  (IOM reachable via management IP)")
    print(f"    {_c(C.BOLD, '3')}  Cancel")
    print()

    while True:
        choice = prompt("Select [1/2/3]")
        if choice in ("1", "2", "3"):
            break
        warn("Please enter 1, 2, or 3.")

    if choice == "1":
        _check_via_serial()
    elif choice == "2":
        _check_via_network()
    # choice == "3" returns to main menu
