"""SNMP handler for Juniper JunOS 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 JUNOS_CHASSIS_FIELD, JUNOS_COMPONENT_FIELD, OID
from .errors import sanitize_snmp_error


class SnmpJunos(ISnmp):
    """SNMP handler for Juniper JunOS devices."""

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

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

        try:
            result_ids = snmp_connection.bulkwalk(OID["JUNOS_CHASSIS"])
            for res_id in result_ids:
                element_id = res_id.oid.replace(f"{OID['JUNOS_CHASSIS']}.", "")
                if element_id not in (JUNOS_CHASSIS_FIELD["NAME"], JUNOS_CHASSIS_FIELD["SERIAL"]):
                    continue
                self._add_to_inventory(inventory, res_id.oid_index, element_id, res_id.value)

            result_ids = snmp_connection.bulkwalk(OID["JUNOS_COMPONENT"])
            for res_id in result_ids:
                element_id = res_id.oid.replace(f"{OID['JUNOS_COMPONENT']}.", "")
                parts = element_id.split(".", 1)
                if len(parts) < 2:
                    continue
                type_id, content_id = parts

                if type_id not in (
                    JUNOS_COMPONENT_FIELD["DESCR"],
                    JUNOS_COMPONENT_FIELD["SERIAL"],
                    JUNOS_COMPONENT_FIELD["PART_NUM"],
                ):
                    continue

                self._add_to_inventory(inventory, f"cont-{content_id}", type_id, res_id.value)

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

    def get_human_readable_inventory(
        self, inventory: Dict[str, Dict[str, str]]
    ) -> List[Dict[str, str]]:
        cf = JUNOS_CHASSIS_FIELD
        jf = JUNOS_COMPONENT_FIELD
        array_of_inventory_items = []

        for element_id, values in inventory.items():
            if element_id == "0":
                serial_num = values.get(cf["SERIAL"], "").strip()
                if ISnmp.is_valid_serial(serial_num):
                    array_of_inventory_items.append(
                        {
                            "entPhysicalSerialNum": serial_num,
                            "entPhysicalModelName": "",
                            "entPhysicalName": values.get(cf["NAME"], "Chassis").strip(),
                            "entPhysicalClass": "chassis",
                            "entPhysicalDescr": "",
                        }
                    )

            elif element_id.startswith("cont-"):
                serial_num = values.get(jf["SERIAL"], "").strip()
                if ISnmp.is_valid_serial(serial_num):
                    descr = values.get(jf["DESCR"], "").strip()
                    array_of_inventory_items.append(
                        {
                            "entPhysicalSerialNum": serial_num,
                            "entPhysicalModelName": values.get(
                                jf["PART_NUM"], ""
                            ).strip(),
                            "entPhysicalName": descr,
                            "entPhysicalClass": "module",
                            "entPhysicalDescr": descr,
                        }
                    )

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