"""DCS base_tag → 현장 계기 자동 유추 알고리즘."""
from __future__ import annotations
import re

from .rules import get_measurement, get_modifier, get_special_prefix


def split_tag(base_tag: str) -> tuple[str, str]:
    """'ficq-6101' → ('ficq', '6101') / 'xv-6124' → ('xv', '6124')."""
    m = re.match(r"^([a-zA-Z]+)-?(.*)", base_tag)
    if not m:
        return base_tag, ""
    return m.group(1).lower(), m.group(2)


def infer_instruments_for_base_tag(
    base_tag: str,
    data_points: list[str],
    area: str,
) -> list[dict]:
    """
    단일 base_tag에 대해 현장 계기 목록을 유추.

    Returns:
        instrument dict 리스트 (§3.1 스키마와 일치).
        매칭 실패 시 1행 포함 (confidence=low, needs_review=True).
    """
    head, loop = split_tag(base_tag)
    if not loop:
        loop = "unknown"

    dp_set = set(data_points) if data_points else set()

    # 규칙에서 어긋난 태그 → unmatched
    # loop에 '_' 포함 (fica-3102_op 등) 또는 'esd' 포함 (lt-9113-lo-esd 등 시스템 포인트)
    if "_" in loop or "esd" in loop.lower():
        return _build_unmatched(base_tag, area, head, loop)

    # 1. 특수 prefix 우선 (정확한 전체 일치)
    sp = get_special_prefix(head)
    if sp:
        return _build_special(head, loop, area, sp, dp_set)

    # 2. 첫 글자 = 측정량
    first = head[0]
    meas = get_measurement(first)
    if not meas:
        return _build_unmatched(base_tag, area, head, loop)

    instruments = []
    has_transmitter = False
    has_controller = False

    # DCS 내부 기능: controller, totalizer, alarm, switch는 현장 계기가 아님
    _dcs_internal_roles = {"controller", "totalizer", "alarm", "switch"}

    # 3. 수식어 글자별로 계기 생성
    for letter in head[1:]:
        mod = get_modifier(letter)
        if not mod:
            continue
        if mod.get("virtual"):
            continue  # I, R은 가상

        role = mod["role"]

        # DCS 내부 기능은 instruments에서 제외
        if role in _dcs_internal_roles:
            if role == "controller":
                has_controller = True
            continue

        inst = _build_instrument(first, meas, role, loop, area, mod, dp_set, base_tag)
        instruments.append(inst)

        if role == "transmitter":
            has_transmitter = True

    # 4. T 글자가 명시 안 됐어도 컨트롤러가 있으면 송신기 암시
    if not has_transmitter and has_controller:
        inst = _build_implicit_transmitter(first, meas, loop, area, dp_set, base_tag)
        instruments.insert(0, inst)
        has_transmitter = True

    # 5. 컨트롤러 → 제어밸브 자동 생성 (auto_pair)
    if has_controller:
        inst = _build_paired_valve(first, meas, loop, area, dp_set, base_tag)
        instruments.append(inst)

    # [H-4 수정] 빈 리스트일 때 implicit transmitter 강제 추가
    # pi-XXXX/ti-XXXX 등 "측정량 + I(virtual)"만 있는 경우 instruments가 비어 있음
    if not instruments:
        inst = _build_implicit_transmitter(first, meas, loop, area, dp_set, base_tag)
        instruments.append(inst)

    # 6. from/to 채우기
    _link_signal_flow(instruments, base_tag, area)

    # 7. confidence 계산
    for inst in instruments:
        inst["confidence"] = _score_confidence(inst, dp_set)
        inst["needs_review"] = inst["confidence"] == "low"

    return instruments


def _build_instrument(
    first_letter: str, meas: str, role: str, loop: str, area: str, mod: dict, dp_set: set, parent_tag: str
) -> dict:
    role_id = _role_to_id(first_letter, role, loop)
    display = f"{first_letter.upper()}{_role_suffix(role).upper()}-{loop.upper()}"
    dps = mod.get("data_points", [])
    matched_dps = [d for d in dps if d in dp_set]

    return {
        "instrument_id": role_id,
        "display_name": display,
        "parent_base_tag": parent_tag,
        "role": role,
        "loop": loop,
        "area": area or "(none)",
        "measures": meas,
        "data_points": ",".join(matched_dps) if matched_dps else "(none)",
        "from": "(none)",
        "to": "(none)",
        "description": "",
        "confidence": "medium",
        "needs_review": False,
        "inference_basis": f"{first_letter.upper()}+{mod.get('role','?')}",
        "operator_notes": "",
        "delete": False,
    }


def _build_special(head: str, loop: str, area: str, sp: dict, dp_set: set) -> list[dict]:
    role = sp["role"]
    meas = sp.get("measures")
    equip_type = sp.get("equipment_type")
    role_id = f"{head}-{loop}"
    display = f"{head.upper()}-{loop.upper()}"

    dp_map = {
        "shutdown-valve": [".instate0", ".instate1"],
        "interlock-relay": [".instate0", ".instate1"],
        "positioner": [".op"],
        "power_equipment": [".run", ".fault"],
    }
    expected_dps = dp_map.get(role, [])
    matched_dps = [d for d in expected_dps if d in dp_set]

    inst = {
        "instrument_id": role_id,
        "display_name": display,
        "parent_base_tag": f"{head}-{loop}",
        "role": role,
        "loop": loop,
        "area": area or "(none)",
        "measures": meas or "(none)",
        "data_points": ",".join(matched_dps) if matched_dps else "(none)",
        "from": "(none)",
        "to": "(none)",
        "description": "",
        "confidence": "medium",
        "needs_review": False,
        "inference_basis": f"special_prefix:{head}",
        "operator_notes": "",
        "delete": False,
    }

    if role == "power_equipment" and equip_type:
        inst["equipment_type"] = equip_type
    return [inst]


def _build_unmatched(base_tag: str, area: str, head: str, loop: str) -> list[dict]:
    return [{
        "instrument_id": f"{head}-{loop}",
        "display_name": f"{head.upper()}-{loop.upper()}",
        "parent_base_tag": base_tag,
        "role": "equipment",
        "loop": loop,
        "area": area or "(none)",
        "measures": "(none)",
        "data_points": "(none)",
        "from": "(none)",
        "to": "(none)",
        "description": "",
        "confidence": "low",
        "needs_review": True,
        "inference_basis": "unmatched_prefix",
        "operator_notes": "",
        "delete": False,
    }]


def _build_implicit_transmitter(first: str, meas: str, loop: str, area: str, dp_set: set, parent: str) -> dict:
    role_id = f"{first}t-{loop}"
    return {
        "instrument_id": role_id,
        "display_name": f"{first.upper()}T-{loop.upper()}",
        "parent_base_tag": parent,
        "role": "transmitter",
        "loop": loop,
        "area": area or "(none)",
        "measures": meas,
        "data_points": ".pv" if ".pv" in dp_set else "(none)",
        "from": f"process/{area or 'unknown'}-{loop}-inlet",
        "to": f"tag/{parent}",
        "description": "",
        "confidence": "medium",
        "needs_review": False,
        "inference_basis": f"{first.upper()}+(implied T)",
        "operator_notes": "",
        "delete": False,
    }


def _build_paired_valve(first: str, meas: str, loop: str, area: str, dp_set: set, parent: str) -> dict:
    role_id = f"{first}cv-{loop}"
    return {
        "instrument_id": role_id,
        "display_name": f"{first.upper()}CV-{loop.upper()}",
        "parent_base_tag": parent,
        "role": "control-valve",
        "loop": loop,
        "area": area or "(none)",
        "measures": "(none)",
        "data_points": ".op" if ".op" in dp_set else "(none)",
        "from": f"tag/{first}ic-{loop}",
        "to": f"process/{area or 'unknown'}-{loop}-downstream",
        "description": "",
        "confidence": "high",
        "needs_review": False,
        "inference_basis": "C -> CV auto_pair",
        "operator_notes": "",
        "delete": False,
    }


def _role_suffix(role: str) -> str:
    mapping = {
        "transmitter": "t",
        "controller": "ic",
        "totalizer": "q",
        "switch": "s",
        "alarm": "a",
        "interlock-relay": "y",
        "positioner": "z",
    }
    return mapping.get(role, role[:2])


def _role_to_id(first_letter: str, role: str, loop: str) -> str:
    return f"{first_letter}{_role_suffix(role)}-{loop}"


def _link_signal_flow(instruments: list[dict], parent_tag: str, area: str) -> None:
    """role별 from/to 기본값 채음 (§5.1)."""
    transmitters = [i for i in instruments if i["role"] == "transmitter"]
    valves = [i for i in instruments if i["role"] == "control-valve"]

    for t in transmitters:
        if t["from"] == "(none)":
            t["from"] = f"process/{area or 'unknown'}-{t['loop']}-inlet"
        if t["to"] == "(none)":
            t["to"] = f"tag/{parent_tag}"

    for v in valves:
        if v["from"] == "(none)":
            v["from"] = f"tag/{parent_tag}"
        if v["to"] == "(none)":
            v["to"] = f"process/{area or 'unknown'}-{v['loop']}-downstream"


def _score_confidence(inst: dict, dp_set: set) -> str:
    """data_point 일치도 + prefix 매칭으로 신뢰도 계산."""
    role = inst["role"]
    basis = inst.get("inference_basis", "")

    if basis == "unmatched_prefix":
        return "low"

    expected = {
        "transmitter": [".pv"],
        "controller": [".sp", ".op"],
        "totalizer": [".qv"],
        "switch": [".instate0"],
        "shutdown-valve": [".instate0"],
    }

    checks = expected.get(role, [])
    if not checks:
        return "medium" if basis.startswith("special") else "high"

    matched = sum(1 for c in checks if c in dp_set)
    if matched == len(checks):
        return "high"
    if matched > 0:
        return "medium"
    return "low"


def _role_to_korean_description(role: str, meas: str) -> str:
    """role + 측정량 → 한국어 설명 초안."""
    meas_ko = {
        "flow": "유량", "pressure": "압력", "temperature": "온도",
        "level": "위차", "analysis": "분석", "speed": "회전수",
        "weight": "중량", "density": "비중", "power": "전력", "moisture": "함량",
    }
    m = meas_ko.get(meas, meas)

    role_desc = {
        "transmitter": f"{m} 송신기",
        "controller": f"{m} 제어기",
        "totalizer": f"{m} 적산기",
        "switch": f"{m} 스위치",
        "alarm": f"{m} 알람",
        "control-valve": f"{m} 제어밸브",
        "shutdown-valve": "차단밸브",
        "interlock-relay": "인터록 릴레이",
        "positioner": "포지셔너",
        "motor": "모터",
        "pump": "펌프",
        "compressor": "압축기",
        "agitator": "교반기",
        "blower": "송풍기",
        "fan": "송풍기",
    }
    return role_desc.get(role, role)
