"""
Update toponode production addresses from containerlab topology data.
"""

import json
import logging
from pathlib import Path

from eda_integration.utils.constants import SUBSTEP_INDENT
from eda_integration.utils.exceptions import ProductionAddressUpdateError
from state import get_topology_data_path

logger = logging.getLogger(__name__)


def _parse_topology_node_addresses(topology_file: str | Path) -> dict[str, str]:
    """
    Read containerlab topology JSON and return node shortname -> mgmt IPv4.
    No dependency on models.
    """
    path = Path(topology_file)
    try:
        data = json.loads(path.read_text())
    except (OSError, json.JSONDecodeError) as e:
        logger.error(f"Failed to load topology file {path}: {e}")
        return {}
    nodes = data.get("nodes") or {}
    out = {}
    for name, node in nodes.items():
        if isinstance(node, dict):
            ip = node.get("mgmt-ipv4-address")
            if ip:
                out[name] = ip
    return out


def run_update_toponode_production_addresses(
    topology_file: str | Path | None = None,
    toponode_dir: str | Path | None = None,
) -> Exception | None:
    """
    Parse the topology JSON and update each node's toponode JSON with productionAddress.ipv4.
    Reads from topology file, for each node reads its JSON file from toponode_dir,
    appends productionAddress, then writes back. Caller must pass topology_file and toponode_dir.
    """
    if topology_file is None or toponode_dir is None:
        raise ProductionAddressUpdateError("topology_file and toponode_dir are required")
    _topology_file = Path(topology_file)
    _toponode_dir = Path(toponode_dir)

    node_to_ip = _parse_topology_node_addresses(_topology_file)
    if not node_to_ip:
        logger.warning("No node addresses from topology; skipping production address update")
        return None

    for node_name, mgmt_ipv4 in node_to_ip.items():
        json_file = _toponode_dir / f"{node_name}.json"
        if not json_file.exists():
            logger.debug(f"No toponode file for node '{node_name}', skipping")
            continue

        try:
            with open(json_file) as f:
                data = json.load(f)
        except (OSError, json.JSONDecodeError) as e:
            logger.error(f"Failed to load {json_file}: {e}")
            continue

        if not isinstance(data, dict):
            data = {}

        if "productionAddress" not in data:
            data["productionAddress"] = {}
        data["productionAddress"]["ipv4"] = mgmt_ipv4

        try:
            with open(json_file, "w") as f:
                json.dump(data, f, indent=2, sort_keys=False)
            logger.info(
                f"{SUBSTEP_INDENT}Updated {json_file.name}: productionAddress.ipv4 = {mgmt_ipv4}"
            )
        except OSError as e:
            logger.error(f"Failed to write {json_file}: {e}")

    return None


def run_update_toponode_addresses_for_stage(stage_dir: str | Path) -> Exception | None:
    """
    Update toponode production addresses using standard stage layout.
    topology_file = stage_dir/clab/<clab-topology*>/topology-data.json,
    toponode_dir = stage_dir/resources/toponode.
    """
    stage = Path(stage_dir).resolve()
    topology_file = get_topology_data_path(stage)
    toponode_dir = stage / "resources" / "toponode"
    return run_update_toponode_production_addresses(topology_file, toponode_dir)
