"""MG DICOM 중에서 분석 대상 CC 2D mammogram만 선별한다 (간단 버전 QC용).

왜 이 모듈이 필요한가:
  Modality=MG 로 묶여 들어오는 영상에는 진짜 유방촬영 외에
  (a) 루닛 등 AI 분석 결과(SecondaryCapture, 흔히 RGB "R CC" 로 태깅),
  (b) tomo(유방 단층촬영, Breast Tomosynthesis / multiframe, 118·160장),
  (c) 컬러/스크린샷
  이 섞여 있다. ViewPosition/SeriesDescription 텍스트는 진짜 영상과 **동일**하게
  "CC"·"R CC" 로 찍히므로 텍스트만으로는 거를 수 없고, **SOPClass 기반 allowlist** 로
  판별해야 한다(실데이터 근거: C:\\PACSDATA\\65288_MG).

선별 규칙(allowlist): Digital Mammography X-Ray Image Storage(2D) 이고, 단일프레임·
  MONOCHROME·SamplesPerPixel=1(이상은 dicom_io.header 가 이미 검증) 이며, ViewPosition=CC,
  laterality=R/L 인 것만 통과. SecondaryCapture / Breast Tomosynthesis 는 명시적으로 제외.

같은 side 에 FOR PRESENTATION 과 FOR PROCESSING 이 함께 오면 PRESENTATION 을 우선 채택
  (PROCESSING 은 제외). PRESENTATION 이 2장 이상이면(진짜 복수 CC) 모두 유지하여
  뒤에서 평균 대상이 되게 한다.
"""
from dicom_io import header, checksum

# Digital Mammography X-Ray Image Storage (2D) — For Presentation / For Processing
SOP_MG_PRESENTATION = '1.2.840.10008.5.1.4.1.1.1.2'
SOP_MG_PROCESSING = '1.2.840.10008.5.1.4.1.1.1.2.1'
ALLOWED_SOP = {SOP_MG_PRESENTATION, SOP_MG_PROCESSING}

# 명시적 배제 대상 (진단 가치 없는 파생물 / 분석 비대상)
SOP_SECONDARY_CAPTURE = '1.2.840.10008.5.1.4.1.1.7'  # 루닛 등 AI 결과 캡처
SOP_BREAST_TOMO = '1.2.840.10008.5.1.4.1.1.13.1.3'   # 유방 단층촬영(DBT)


def classify_one(raw):
    """단일 DICOM 바이트가 '분석 대상 CC' 인지 판정한다.

    통과하면 (meta dict, None), 탈락하면 (None, 사유 문자열) 을 돌려준다.
    meta 는 dicom_io.header 의 반환으로, side/view/sop_class/intent 등을 포함한다.
    """
    try:
        # header 가 비-MG / 다중프레임(tomo multiframe) / 컬러(RGB SC) / 비-MONOCHROME 을
        # 이미 ValueError 로 거른다. 통과하면 단일프레임 grayscale MG 임이 보장된다.
        meta, _ = header(raw)
    except Exception as exc:
        # 손상/비-DICOM 파일은 pydicom 이 InvalidDicomError 등(ValueError 아님)을 던질 수 있으므로
        # 넓게 잡아 해당 1장만 제외한다(한 장 때문에 전체 선별이 중단되지 않도록).
        return None, f'헤더 거부: {exc}'

    sop = meta.get('sop_class', '')
    if sop == SOP_SECONDARY_CAPTURE:
        return None, 'SecondaryCapture(AI 결과 등) 제외'
    if sop == SOP_BREAST_TOMO:
        return None, 'Breast Tomosynthesis(tomo) 제외'
    if sop not in ALLOWED_SOP:
        # 단일프레임이라도 2D 유방촬영 SOPClass 가 아니면(예: 알 수 없는 파생물) 제외.
        return None, f'허용되지 않은 SOPClass: {sop or "(없음)"}'
    if meta.get('view') != 'CC':
        return None, f'CC 아님(view={meta.get("view") or "미확인"})'
    if meta.get('side') not in ('L', 'R'):
        return None, f'laterality 불명(side={meta.get("side") or "미확인"})'
    return meta, None


def select_cc(items):
    """[(name, raw), ...] 를 받아 side 별 CC 레코드로 선별한다.

    반환: (accepted, rejected)
      accepted = {'R': [rec, ...], 'L': [rec, ...]}  (side 별 FOR PRESENTATION 우선 dedup 적용)
      rejected = [(name, 사유), ...]
      rec = {'name', 'raw', 'digest', 'meta', 'intent'}
    """
    accepted = {'R': [], 'L': []}
    rejected = []
    for name, raw in items:
        meta, reason = classify_one(raw)
        if meta is None:
            rejected.append((name, reason))
            continue
        accepted[meta['side']].append({
            'name': name,
            'raw': raw,
            'digest': checksum(raw),
            'meta': meta,
            'intent': meta.get('intent', 'UNKNOWN'),
        })

    # side 별 FOR PRESENTATION 우선: presentation 이 하나라도 있으면 processing 은 버린다.
    # (같은 촬영의 raw/processed 중복을 제거. presentation 이 없으면 processing 을 그대로 사용.)
    for side in ('R', 'L'):
        recs = accepted[side]
        if any(r['intent'] == 'FOR PRESENTATION' for r in recs):
            kept = []
            for r in recs:
                if r['intent'] == 'FOR PRESENTATION':
                    kept.append(r)
                else:
                    rejected.append((r['name'], 'FOR PRESENTATION 우선 — PROCESSING 중복 제외'))
            accepted[side] = kept

    return accepted, rejected
