"""Driver for Dymind hematology analyzers (DH36 and family) — HL7 over MLLP.

Dymind result messages are HL7 ORU^R01:
  MSH | ... | ORU^R01 | ...
  PID | ...
  OBR | 1 | <placer> | <sampleId> | ...
  OBX | 1 | NM | WBC^WBC | | 7.4 | 10*3/uL | ... | N | | | F
  OBX | 2 | NM | RBC^RBC | | 5.10| 10*6/uL | ... | N | | | F
  ...

We extract the Sample ID (barcode) from the configured field path(s) and one
result per OBX whose value type is numeric/string (skip ED graphs/histograms).
"""

from __future__ import annotations

from typing import Any, Dict, List

from .. import hl7

# This driver speaks MLLP-framed HL7.
is_mllp = True


def _extract_barcode(msg: "hl7.Message", barcode_fields: List[str]) -> str:
    """Return the first non-empty value among the configured field paths."""
    for path in barcode_fields:
        value = hl7.get_path(msg, path).strip()
        if value:
            return value
    return ""


def parse(raw: str, device_cfg: Any) -> Dict[str, Any]:
    """Parse one raw HL7 message into a normalised dict.

    Returns:
        {
          "type": "result" | "query" | "other",
          "control_id": str,
          "barcode": str,
          "results": [ {test_code, raw_result, unit, flag, value_type, raw_obx} ],
          "raw": str,
        }
    """
    msg = hl7.Message(raw)
    mtype = msg.message_type.upper()

    if mtype in ("QRY", "QBP", "ORM"):
        # Bidirectional query: the analyzer asks for a sample's orders.
        return {
            "type": "query",
            "control_id": msg.control_id,
            "barcode": _extract_barcode(msg, device_cfg.barcode_fields),
            "results": [],
            "raw": raw,
        }

    if mtype not in ("ORU", "OUL"):
        return {"type": "other", "control_id": msg.control_id, "barcode": "",
                "results": [], "raw": raw}

    barcode = _extract_barcode(msg, device_cfg.barcode_fields)
    allowed_types = set(device_cfg.value_types)
    results: List[Dict[str, Any]] = []

    for obx in msg.all("OBX"):
        value_type = obx.field(2).strip().upper()
        if allowed_types and value_type not in allowed_types:
            continue  # skip encapsulated data (ED) such as graphs/histograms
        test_code = obx.component(3, 1).strip()
        raw_result = obx.field(5).strip()
        if not test_code or raw_result == "":
            continue
        results.append(
            {
                "test_code": test_code,
                "raw_result": raw_result,
                "unit": obx.component(6, 1).strip(),
                "flag": obx.field(8).strip(),
                "value_type": value_type,
                "raw_obx": obx.raw,
            }
        )

    return {
        "type": "result",
        "control_id": msg.control_id,
        "barcode": barcode,
        "results": results,
        "raw": raw,
    }


def build_ack(raw: str, code: str = "AA") -> str:
    """Build an HL7 ACK for an inbound message."""
    return hl7.build_ack(hl7.Message(raw), code)
