"""SNMP handler for Nokia SROS devices."""

from typing import Dict, List, Optional

from easysnmp import Session
from easysnmp.exceptions import EasySNMPError

from inventory_monitor.logger import logger

from .base import ISnmp
from .constants import OID, SROS_CHASSIS_FIELD, SROS_HW_CLASS_CHASSIS, SROS_HW_CLASS_MODULE
from .errors import sanitize_snmp_error


class SnmpSros(ISnmp):
    """SNMP handler for Nokia SROS devices."""

    def get_snmp_connection(
        self, device_name: str, device_ip: str
    ) -> Optional[Session]:
        return self.snmp_session(device_name, device_ip, OID["SROS_BASE"])

    def get_snmp_inventory(
        self, snmp_connection: Session
    ) -> Optional[Dict[str, Dict[str, str]]]:
        inventory = {}

        try:
            result_ids = snmp_connection.bulkwalk(OID["SROS_CHASSIS"])
            for res_id in result_ids:
                element_id = res_id.oid.replace(f"{OID['SROS_CHASSIS']}.", "")
                self._add_to_inventory(inventory, res_id.oid_index, element_id, res_id.value)

            result_ids = snmp_connection.bulkwalk(OID["SROS_PORT"])
            for res_id in result_ids:
                port_key = f"port-{res_id.oid_index}"
                element_id = res_id.oid.replace(f"{OID['SROS_PORT']}.", "")
                self._add_to_inventory(inventory, port_key, element_id, res_id.value)

            return inventory
        except (SystemError, EasySNMPError) as e:
            logger.error(f"Error retrieving SROS inventory: {sanitize_snmp_error(e)}")
            return None

    def get_human_readable_inventory(
        self, inventory: Dict[str, Dict[str, str]]
    ) -> List[Dict[str, str]]:
        sf = SROS_CHASSIS_FIELD
        array_of_inventory_items = []

        port_type_map = {
            "1": "GBIC transceiver",
            "3": "SFP transceiver",
            "4": "XBI transceiver",
            "5": "XENPAK transceiver",
            "6": "XFP transceiver",
            "7": "XFF transceiver",
            "8": "XFPE transceiver",
            "9": "XPAK transceiver",
            "10": "X2 transceiver",
            "11": "DWDM SFP transceiver",
            "12": "QSFP transceiver",
            "13": "QSFP+ transceiver",
            "14": "CFP transceiver",
        }

        for element_id, values in inventory.items():
            if element_id.startswith("port-") and values.get(sf["PORT_SERIAL"]):
                serial_num = values[sf["PORT_SERIAL"]].strip()
                if ISnmp.is_valid_serial(serial_num):
                    port_type = values.get(sf["PORT_TYPE"], "")
                    array_of_inventory_items.append(
                        {
                            "entPhysicalSerialNum": serial_num,
                            "entPhysicalModelName": values.get(
                                sf["PORT_MODEL"], ""
                            ).strip(),
                            "entPhysicalName": values.get(
                                sf["PORT_NAME"], ""
                            ).strip(),
                            "entPhysicalClass": "port",
                            "entPhysicalDescr": port_type_map.get(
                                str(port_type), "(unknown transceiver)"
                            ),
                        }
                    )

            elif values.get(sf["SERIAL"]):
                hw_class = values.get(sf["HW_CLASS"], "").strip()
                serial_num = values.get(sf["SERIAL"], "").strip()

                if ISnmp.is_valid_serial(serial_num):
                    try:
                        hw_class_int = int(hw_class)
                    except (ValueError, TypeError):
                        hw_class_int = -1

                    if hw_class_int in SROS_HW_CLASS_CHASSIS:
                        phys_class = "chassis"
                    elif hw_class_int in SROS_HW_CLASS_MODULE:
                        phys_class = "-"
                    else:
                        continue

                    array_of_inventory_items.append(
                        {
                            "entPhysicalSerialNum": serial_num,
                            "entPhysicalModelName": values.get(
                                sf["MODEL"], ""
                            ).strip(),
                            "entPhysicalName": values.get(
                                sf["COMPONENT_NAME"], ""
                            ).strip(),
                            "entPhysicalClass": phys_class,
                            "entPhysicalDescr": "",
                        }
                    )

        if not array_of_inventory_items:
            logger.debug(
                f"SROS: No items with valid serial numbers found "
                f"(raw inventory had {len(inventory)} entries)"
            )
        return array_of_inventory_items
