"""Bosch column-name structure parsing.

Every measurement column encodes where on the factory floor it was taken:
    L3_S36_F3939  ->  line 3, station 36, feature 3939
    L3_S36_D3921  ->  line 3, station 36, date channel 3921

That naming is the whole reason this dataset can be turned into a process
graph at all — the structure is in the schema, not in a separate routing
table. SECOM's sensors are anonymized and admit no such reconstruction.
"""
from __future__ import annotations

import re
from collections import defaultdict

COL_RE = re.compile(r"^L(?P<line>\d+)_S(?P<station>\d+)_(?P<kind>[FD])(?P<idx>\d+)$")


def parse_column(col: str) -> dict | None:
    """-> {'line': int, 'station': int, 'kind': 'F'|'D', 'idx': int} or None
    for columns that carry no structure (Id, Response)."""
    m = COL_RE.match(col)
    if not m:
        return None
    return {"line": int(m["line"]), "station": int(m["station"]),
            "kind": m["kind"], "idx": int(m["idx"])}


def station_key(line: int, station: int) -> str:
    """Canonical node name for the process graph."""
    return f"L{line}_S{station}"


def group_by_station(columns) -> dict[str, list[str]]:
    """Map station key -> the columns measured at that station, preserving
    the file's column order."""
    groups: dict[str, list[str]] = defaultdict(list)
    for c in columns:
        p = parse_column(c)
        if p:
            groups[station_key(p["line"], p["station"])].append(c)
    return dict(groups)


def sort_station_keys(keys) -> list[str]:
    """Sort station keys by (line, station) rather than lexically, so
    L0_S9 precedes L0_S10."""
    def k(s: str):
        p = parse_column(s + "_F0")
        return (p["line"], p["station"])
    return sorted(keys, key=k)
