# -*- coding: utf-8 -*-
"""URL 패턴으로 각 사이트의 CMS 지문 산출 → 검수완료(정답) vs 전개대상 커버리지 매핑. (시트만 읽음, 무fetch)"""
import openpyxl, os, re, sys, glob
from collections import Counter
sys.stdout.reconfigure(encoding='utf-8')
ROOT = r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵'
EXCLUDE = {'서천군'}
INSPECTED = {'계룡시','공주시','금산군','논산시','보령시','당진시','부여군',
             '고창군','김제시','남원시','군산시','무주군'}

def pick_xlsx(folder):
    cand=[f for f in glob.glob(os.path.join(folder,'*.xlsx'))
          if 'backup' not in os.path.basename(f).lower()
          and not os.path.basename(f).startswith(('_','~$'))]
    cand.sort(key=lambda f:(0 if re.match(r'^[가-힣]+도?_',os.path.basename(f)) else 1,len(f)))
    return cand[0] if cand else None

def url_sig(u):
    """URL을 CMS 패턴 토큰으로 환원."""
    u=u.lower()
    toks=[]
    if 'selectboardlist' in u or 'bbsmstr' in u: toks.append('bbs:BBSMSTR')
    elif '/cop/bbs/' in u: toks.append('bbs:cop')
    elif 'selectbbsnttlist' in u or 'bbsno=' in u: toks.append('bbs:nttList')
    elif '/bbs/' in u and 'list.do' in u: toks.append('bbs:bbsList')
    if 'menucd=' in u: toks.append('pg:menuCd')
    elif 'contents.do?key=' in u or re.search(r'/sub\.do\?.*key=',u): toks.append('pg:key')
    elif re.search(r'/kor/sub[\d_]+\.do',u) or re.search(r'/[a-z]{2,4}/sub[\d_]+\.do',u): toks.append('pg:subNN.do')
    elif 'prog/' in u and 'list.do' in u: toks.append('pg:prog.list')
    elif re.search(r'/\w+\.do\b',u): toks.append('pg:.do')
    elif '.jsp' in u: toks.append('pg:.jsp')
    elif '.php' in u: toks.append('pg:.php')
    return toks

def fingerprint(xlsx):
    wb=openpyxl.load_workbook(xlsx); ws=wb.active
    cnt=Counter(); host=Counter()
    for r in range(3,ws.max_row+1):
        c=ws.cell(r,11)
        u=c.hyperlink.target if c.hyperlink else (c.value if isinstance(c.value,str) and c.value.startswith('http') else None)
        if not u: continue
        m=re.match(r'https?://([^/]+)',u)
        if m: host[re.sub(r'^www\.','',m.group(1))]+=1
        for t in url_sig(u): cnt[t]+=1
    dom=host.most_common(1)[0][0] if host else '?'
    # 시그니처 = 상위 토큰 집합
    sig=tuple(sorted(t for t,n in cnt.items() if n>=3))
    return dom, sig, cnt

sites=[]
for prov in sorted(os.listdir(ROOT)):
    pdir=os.path.join(ROOT,prov)
    if not os.path.isdir(pdir) or prov.startswith('__'): continue
    for d in sorted(os.listdir(pdir)):
        sdir=os.path.join(pdir,d)
        if not os.path.isdir(sdir): continue
        nm=re.sub(r'^\d+\.','',d)
        if nm in EXCLUDE: continue
        xl=pick_xlsx(sdir)
        if not xl: continue
        try: dom,sig,cnt=fingerprint(xl)
        except Exception as e: sig=('ERR',); dom=str(e)[:30]; cnt={}
        sites.append({'prov':prov[:2],'name':nm,'dom':dom,'sig':sig,
                      'inspected':nm in INSPECTED})

# group by sig
from collections import defaultdict
groups=defaultdict(list)
for s in sites: groups[s['sig']].append(s)
print('=== CMS 시그니처별 그룹 (★=검수완료 선생님 있음) ===')
for sig,ss in sorted(groups.items(),key=lambda x:-len(x[1])):
    has_teacher=any(s['inspected'] for s in ss)
    teach=[s['name'] for s in ss if s['inspected']]
    targets=[f"{s['name']}({s['prov']})" for s in ss if not s['inspected']]
    star='★' if has_teacher else '✗미지'
    print(f"\n[{star}] sig={'+'.join(sig) if sig else '(무)'}  ({len(ss)}곳)")
    if teach: print(f"   선생님(검수완료): {', '.join(teach)}")
    if targets: print(f"   전개대상: {', '.join(targets)}")
    if not has_teacher and targets:
        print(f"   ⚠ 정답 선생님 없음 → 미지CMS(별도조사 필요)")
