import sys, io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
import openpyxl
from openpyxl.utils import get_column_letter
PKG = r'D:\01.프로젝트\DB수집\작업파일\_제출패키지_2026-06-03\프리랜서1\충청남도_부여군.xlsx'
ws = openpyxl.load_workbook(PKG).active

# merge lookup
merges = list(ws.merged_cells.ranges)
def merge_of(r,c):
    for m in merges:
        if m.min_row<=r<=m.max_row and m.min_col<=c<=m.max_col:
            return m
    return None
def eff(r,c):
    m=merge_of(r,c)
    if m: return ws.cell(m.min_row,m.min_col).value
    return ws.cell(r,c).value

data=[r for r in range(3,ws.max_row+1) if ws.cell(r,2).value not in (None,'')]
issues=[]

# 1) header merges
hdr={str(m) for m in merges}
for h in ('B1:R1','S1:W1','Y1:AA1'):
    if h not in hdr: issues.append(f'헤더병합 누락 {h}')

# 2) G+ no merge (col>=7)
gplus=[str(m) for m in merges if m.min_col>=7 and m.min_row>=3]
if gplus:
    issues.append(f'[규칙위반] G이상 병합 {len(gplus)}개: {gplus[:20]}')

# helper: maximal runs of equal non-None value over a row list for a column
def runs(rows, col):
    out=[];
    for r in rows:
        v=eff(r,col)
        if out and out[-1][2]==v and v not in (None,'') and r==out[-1][1]+1:
            out[-1][1]=r
        else:
            out.append([r,r,v])
    return out

def exact_merge(col,a,z):
    m=merge_of(a,col)
    return m is not None and m.min_col==col==m.max_col and m.min_row==a and m.max_row==z

# 3) D column
druns=runs(data,4)
nD=0
for a,z,v in druns:
    if v in (None,''):
        issues.append(f'D 빈값 행 r{a}'); continue
    nD+=1
    if z>a and not exact_merge(4,a,z):
        m=merge_of(a,4)
        issues.append(f'D런 {v!r} r{a}:{z} 병합불일치(현재 {m})')
    if z==a:
        m=merge_of(a,4)
        if m and (m.max_row>m.min_row):
            issues.append(f'D 단일행 r{a} {v!r} 인데 병합 {m}')
# split check: same D value appearing in non-adjacent runs
from collections import Counter
dval_runs=Counter(v for a,z,v in druns if v not in (None,''))
for v,cnt in dval_runs.items():
    if cnt>1:
        issues.append(f'[주의] D값 {v!r} 이 {cnt}개 비연속 런으로 분리됨')

# 4) E within each D-run
for a,z,dv in druns:
    drows=[r for r in data if a<=r<=z]
    for ea,ez,ev in runs(drows,5):
        if ev in (None,''): continue
        # E merge must be within D-run
        if ez>ea and not exact_merge(5,ea,ez):
            m=merge_of(ea,5)
            issues.append(f'E런 {ev!r} r{ea}:{ez}(D={dv!r}) 병합불일치(현재 {m})')
        m=merge_of(ea,5)
        if m and (m.min_row<a or m.max_row>z):
            issues.append(f'[규칙위반] E병합 {m}이 D그룹 r{a}:{z}({dv!r}) 경계 넘음')
        if ez==ea and m and m.max_row>m.min_row:
            issues.append(f'E 단일행 r{ea} {ev!r} 인데 병합 {m}')

# 5) F within each D+E sub-run
for a,z,dv in druns:
    drows=[r for r in data if a<=r<=z]
    for ea,ez,ev in runs(drows,5):
        erows=[r for r in drows if ea<=r<=ez] if ev not in (None,'') else [r for r in drows if eff(r,5) in (None,'')]
        # group F within this E sub-run
        sub=[r for r in drows if ea<=r<=ez]
        for fa,fz,fv in runs(sub,6):
            if fv in (None,''): continue
            if fz>fa and not exact_merge(6,fa,fz):
                m=merge_of(fa,6)
                issues.append(f'F런 {fv!r} r{fa}:{fz}(D={dv!r},E={ev!r}) 병합불일치(현재 {m})')
            m=merge_of(fa,6)
            if m and (m.min_row<ea or m.max_row>ez):
                issues.append(f'[규칙위반] F병합 {m}이 D+E그룹 r{ea}:{ez} 경계 넘음')

# 6) merge overlaps
seen={}; overlap=0
for m in merges:
    for r in range(m.min_row,m.max_row+1):
        for c in range(m.min_col,m.max_col+1):
            if (r,c) in seen: overlap+=1
            seen[(r,c)]=1
if overlap: issues.append(f'병합 겹침 셀 {overlap}')

print(f'데이터행 {len(data)}  대분류(D런) {nD}개  총병합 {len(merges)}')
print(f'D 병합수={sum(1 for m in merges if m.min_col==4)} E={sum(1 for m in merges if m.min_col==5)} F={sum(1 for m in merges if m.min_col==6)} G+={len(gplus)}')
print('대분류 목록:', [v for a,z,v in druns])
print()
if issues:
    print(f'⚠ 병합 이슈 {len(issues)}건:')
    for x in issues: print('  ✗', x)
else:
    print('✅ 병합 규칙 위반 없음 (D/E/F 경계·연속병합·G무병합·헤더병합 정상)')
