"""
Physical patch panel: front-panel position count for the rack (field workflow).

Stored in fiber_map.json as ``patch_panel``: ``{ "slots": N, "label": "…" }``.
USB hub calibrate still walks hub ports; map keys 1…N align with panel positions.
"""

from __future__ import annotations

import os
from dataclasses import dataclass
from typing import Any, Dict, Optional

from fiwi.constants import PANEL_SLOTS

_MAX_SLOTS = 256


def default_panel_ports() -> int:
    """
    Fallback front-panel **port** count from ``FIWI_DEFAULT_PANEL_PORTS`` (``config/*.ini`` ``[patch_panel] default_ports``),
    or legacy ``FIWI_DEFAULT_PANEL_SLOTS``, else ``PANEL_SLOTS``.
    """
    raw = (os.environ.get("FIWI_DEFAULT_PANEL_PORTS") or os.environ.get("FIWI_DEFAULT_PANEL_SLOTS") or "").strip()
    if raw.isdigit():
        n = int(raw)
        if 1 <= n <= _MAX_SLOTS:
            return n
    return PANEL_SLOTS


@dataclass(frozen=True)
class PatchPanel:
    """Instantiated panel: ``slots`` front-panel positions (numbered 1…slots)."""

    slots: int
    label: str = ""

    def __post_init__(self) -> None:
        if self.slots < 1:
            raise ValueError("patch panel slots must be >= 1")
        if self.slots > _MAX_SLOTS:
            raise ValueError(f"patch panel slots must be <= {_MAX_SLOTS}")

    def to_map_blob(self) -> Dict[str, Any]:
        d: Dict[str, Any] = {"slots": self.slots}
        if self.label.strip():
            d["label"] = self.label.strip()
        return d

    @classmethod
    def from_map_blob(cls, blob: Any) -> Optional[PatchPanel]:
        if not isinstance(blob, dict):
            return None
        s = blob.get("slots")
        if isinstance(s, str) and s.strip().isdigit():
            s = int(s.strip())
        if not isinstance(s, int) or s < 1:
            return None
        if s > _MAX_SLOTS:
            return None
        lab = blob.get("label")
        label = lab.strip() if isinstance(lab, str) else ""
        return cls(slots=s, label=label)


def effective_panel_slots(doc: Optional[Dict[str, Any]]) -> int:
    """Panel position count from ``doc['patch_panel']``, else :func:`default_panel_ports`."""
    if not isinstance(doc, dict):
        return default_panel_ports()
    pp = PatchPanel.from_map_blob(doc.get("patch_panel"))
    if pp is not None:
        return pp.slots
    return default_panel_ports()
