"""
계기 EU range 룩업 생성 → instrument_ranges.json (오프라인 분석 전용).

소스 우선순위 (사용자 확정 2026-06-06):
  1) realtime_table PV_HighRange/PV_LowRange — 컨트롤러 실측값(권위). online 컬럼(5·6·8차).
  2) xlsx InstructionsDisplayNumber       — Experion 포인트 설정. online 안 된 컬럼(9·10차=C4) fallback.

★ 이 JSON은 오프라인 분석(prodmap/export_model 등)의 스파이크 클린징 입력 전용.
  C# 운영 코드(SteamAdvisor)는 절대 이걸 안 씀 — realtime range를 live로 직접 읽음.

교차검증(2026-06-06): realtime vs xlsx 23/32 일치. 8차는 xlsx가 미동기(1000 vs 실측 18000)
  → online 컬럼은 realtime이 권위, xlsx는 미연결 컬럼만.
"""
import json
import os
import openpyxl
import psycopg

BASE = os.path.dirname(os.path.abspath(__file__))
XLSX = os.path.join(BASE, "..", "..", "docs", "Sinam_Tag_all.xlsx")
OUT = os.path.join(BASE, "instrument_ranges.json")
DSN_IIOT = "host=localhost port=5432 dbname=iiot_platform user=postgres password=postgres"
DEFAULT_LO = -5.0   # xlsx fallback의 LowRange (realtime 실측 대부분 -5~0)


def realtime_ranges():
    """5·6·8차 등 online 컬럼의 실측 EU range (권위)."""
    out = {}
    with psycopg.connect(DSN_IIOT) as c:
        cur = c.cursor()
        cur.execute("""
            SELECT split_part(tagname,'.',1) AS base,
                   max(CASE WHEN tagname LIKE '%PV_HighRange' THEN livevalue::float END) AS hi,
                   max(CASE WHEN tagname LIKE '%PV_LowRange'  THEN livevalue::float END) AS lo
            FROM hc900.realtime_table
            WHERE tagname LIKE '%PV_%Range'
            GROUP BY 1
        """)
        for base, hi, lo in cur.fetchall():
            if hi is not None:
                out[base] = {"lo": lo if lo is not None else DEFAULT_LO,
                             "hi": hi, "src": "realtime"}
    return out


def xlsx_ranges():
    """InstructionsDisplayNumber = range hi (Experion 포인트 설정)."""
    wb = openpyxl.load_workbook(XLSX, read_only=True, data_only=True)
    ws = wb["Sheet1"]
    rows = ws.iter_rows(values_only=True)
    next(rows)                       # 머지셀 None 행
    header = list(next(rows))        # 실제 헤더
    ni = header.index("ItemName")
    idn = header.index("InstructionsDisplayNumber")
    out = {}
    for r in rows:
        nm, v = r[ni], r[idn]
        if not nm or not isinstance(v, (int, float)) or isinstance(v, bool):
            continue                 # 숫자(range hi)만 — 'default' 등 문자열 스킵
        base = nm.split(".")[0]
        if base not in out:          # 첫 숫자값 채택
            out[base] = float(v)
    return out


def main():
    rt = realtime_ranges()
    xl = xlsx_ranges()
    ranges = dict(rt)                # realtime 우선
    n_xlsx = 0
    for base, hi in xl.items():
        if base not in ranges:       # online 안 된 컬럼만 xlsx fallback
            ranges[base] = {"lo": DEFAULT_LO, "hi": hi, "src": "xlsx"}
            n_xlsx += 1
    with open(OUT, "w") as f:
        json.dump(ranges, f, indent=2, sort_keys=True)
    print(f"realtime(실측) {len(rt)}개 + xlsx(fallback) {n_xlsx}개 = 총 {len(ranges)}개 태그")
    print(f"저장: {OUT}")
    # 9·10차 샘플 확인
    for b in ["FICQ-9201", "FICQ-9218", "FICQ-10201", "FICQ-10218"]:
        print(f"  {b}: {ranges.get(b)}")


if __name__ == "__main__":
    main()
