from dataclasses import dataclass
import logging
import os
from typing import Dict, Optional
from .netmeta_config_loader import load_config
from preseem_protobuf.network_poller import network_poller_pb2

HW_SPECS_FILENAME = "pon_specs.yaml"


@dataclass
class PonClassSpec():
    pon_technology: int
    distance: Optional[int] = None
    max_split: Optional[int] = None
    min_loss: Optional[float] = None  # dBm
    max_loss: Optional[float] = None  # dBm


def load_yaml_specs(filename):
    path = os.path.join(os.path.dirname(os.path.realpath(__file__)), filename)
    try:
        cfg = load_config(path)
        if cfg is None:
            logging.warning(f"Config file {filename} returned None")
            return {}
        return cfg
    except Exception as e:
        logging.error(f"Failed to load {filename}: {e}")
        return {}


_PON_TYPE_MAP = {
    "GPON": network_poller_pb2.PonTechnology.GPON,
    "XGS-PON": network_poller_pb2.PonTechnology.XGS_PON,
    "EPON": network_poller_pb2.PonTechnology.EPON,
    "TEN-G-EPON": network_poller_pb2.PonTechnology.TEN_G_EPON,
}


def _parse_specs_to_pon_class_specs(pon_type, pon_specs):
    if not pon_specs:
        return None, None

    pon_technology = _PON_TYPE_MAP.get(pon_type)
    if pon_technology is None:
        return None, None

    pon_class_spec = PonClassSpec(
        pon_technology=pon_technology,
        distance=pon_specs.get('distance'),
        max_split=pon_specs.get('max_split'),
        min_loss=pon_specs.get('rx_power_min'),
        max_loss=pon_specs.get('rx_power_max'),
    )

    return pon_technology, pon_class_spec


def load_generic_specs():
    """
    Load the YAML input file and assign generic GPON, XGS-PON and Combo PON specs
    """
    cfg = load_yaml_specs(HW_SPECS_FILENAME)
    if not cfg:
        return {}

    class_specs = {}
    class_specs["GPON_BITRATE_DOWN"] = cfg.get("GPON_BITRATE_DOWN")
    class_specs["GPON_BITRATE_UP"] = cfg.get("GPON_BITRATE_UP")
    class_specs["XGSPON_BITRATE"] = cfg.get("XGSPON_BITRATE")
    class_specs["COMBOPON_SPLITRATIO"] = cfg.get("COMBOPON_SPLITRATIO")

    return class_specs


def load_all_hw_class_specs():
    """
    Load the YAML input file and convert the OLT/ONT PartSpecs to the PonClassSpec format.

    Use this function when loading a YAML input file with PartSpecs for OLT, ONT and PONs.
    """
    cfg = load_yaml_specs(HW_SPECS_FILENAME)

    if "PartSpecs" not in cfg:
        return {}

    class_specs = {}
    if "OLT" in cfg["PartSpecs"]:
        olt_pon_specs_dict = cfg["PartSpecs"]["OLT"]
        for olt_name, pon_types in olt_pon_specs_dict.items():
            if olt_name in class_specs:
                logging.warning(
                    f"Duplicate model name '{olt_name}' across OLT/ONT/PON in {HW_SPECS_FILENAME}"
                )
                continue
            else:
                class_specs[olt_name] = {}

            for pon_type, pon_specs in pon_types.items():
                pon_technology, pon_class_spec = _parse_specs_to_pon_class_specs(
                    pon_type, pon_specs)
                if not pon_class_spec:
                    logging.warning(
                        f"Unknown PON technology type '{pon_type}' for OLT '{olt_name}' in {HW_SPECS_FILENAME}"
                    )
                    continue

                class_specs[olt_name][pon_technology] = pon_class_spec

    if "ONT" in cfg["PartSpecs"]:
        ont_pon_specs_dict = cfg["PartSpecs"]["ONT"]
        for ont_name, pon_types in ont_pon_specs_dict.items():
            if ont_name in class_specs:
                logging.warning(
                    f"Duplicate model name '{ont_name}' across OLT/ONT/PON in {HW_SPECS_FILENAME}"
                )
                continue
            else:
                class_specs[ont_name] = {}

            for pon_type, pon_specs in pon_types.items():
                pon_technology, pon_class_spec = _parse_specs_to_pon_class_specs(
                    pon_type, pon_specs)
                if not pon_class_spec:
                    logging.warning(
                        f"Unknown PON technology type '{pon_type}' for ONT '{ont_name}' in {HW_SPECS_FILENAME}"
                    )
                    continue

                class_specs[ont_name][pon_technology] = pon_class_spec

    if "PON" in cfg["PartSpecs"]:
        pon_specs_dict = cfg["PartSpecs"]["PON"]
        for pon_name, pon_types in pon_specs_dict.items():
            if pon_name in class_specs:
                logging.warning(
                    f"Duplicate model name '{pon_name}' across OLT/ONT/PON in {HW_SPECS_FILENAME}"
                )
                continue
            else:
                class_specs[pon_name] = {}

            for pon_type, pon_specs in pon_types.items():
                pon_technology, pon_class_spec = _parse_specs_to_pon_class_specs(
                    pon_type, pon_specs)
                if not pon_class_spec:
                    logging.warning(
                        f"Unknown PON technology type '{pon_type}' for PON '{pon_name}' in {HW_SPECS_FILENAME}"
                    )
                    continue

                class_specs[pon_name][pon_technology] = pon_class_spec

    return class_specs
