import numpy as np

LABELS={'area_cm2':'유방 투영 면적 (cm²)','extent_x_mm':'가로 범위 (mm)','extent_y_mm':'세로 범위 (mm)',
        'thickness_mm':'압박두께 (mm)','volume_ml':'체적 근사치 (mL)','density_percent':'면적밀도 (%)'}

def descriptors(mask,spacing,meta,density):
    result={k:None for k in LABELS};result['density_percent']=density;result['thickness_mm']=meta['thickness_mm']
    if spacing:
        dy,dx=spacing;y,x=np.where(mask)
        if not len(x):raise ValueError('빈 breast mask')
        result.update(area_cm2=float(mask.sum()*dy*dx/100),extent_x_mm=float((x.max()-x.min()+1)*dx),
                      extent_y_mm=float((y.max()-y.min()+1)*dy))
        if meta['view'] in ('CC','MLO') and meta['thickness_mm'] is not None:
            result['volume_ml']=result['area_cm2']*meta['thickness_mm']/10
    return result

def empirical_percentile(value,values,min_n=30):
    if value is None:return None,0
    a=np.asarray(values,float);a=a[np.isfinite(a)]
    n=len(a)
    if n<min_n:return None,n
    return float(100*(np.sum(a<value)+.5*np.sum(a==value))/n),n

def validate_options(options):
    if not isinstance(options,list) or len(options)!=10:raise ValueError('옵션은 정확히 10개가 필요합니다.')
    levels=[o.get('level') for o in options]
    if any(type(v) is not int for v in levels) or sorted(levels)!=list(range(1,11)):raise ValueError('단계는 정수 1–10 각각 한 번이어야 합니다.')
    for o in options:
        for key in ('depth_mm','frequency_MHz','probe_skin_distance_mm'):
            value=o.get(key)
            if value is not None and (isinstance(value,bool) or not isinstance(value,(int,float)) or not np.isfinite(value) or value<0):
                raise ValueError(f'{key}: 0 이상의 유한수 또는 null 필요')
    return options

def recommend(features):
    """Future validated classifier interface. Do not infer levels from size/density yet."""
    return {'level':None,'model_version':None,'reason':'자동 선택 미구현'}

def validate_study(headers,allow_missing=False):
    ids=[identity for _,identity in headers]
    known_studies={v[2] for v in ids if v[2]};known_patients={(v[0],v[1]) for v in ids if v[0]}
    if len(known_studies)>1 or len(known_patients)>1:raise ValueError('다른 환자 또는 다른 검사 DICOM이 섞였습니다.')
    if not allow_missing and any(not v[0] or not v[2] for v in ids):raise ValueError('환자/검사 ID 누락: 같은 검사인지 직접 확인하고 sidebar 확인란을 선택하세요.')
