# -*- coding: utf-8 -*-
"""금산군 메뉴 랜딩행 M = 서브트리 전체 leaf 합 (2026-05-31, 사용자 규칙)
여성가족=10 패턴: 메뉴(6자리 prefix)의 모든 자식페이지 X01..X0N에 대해
  leaf = 라이브 ui-nav_tabs 개수 (탭 없으면 1)
M(랜딩행) = Σ leaf.  단 메뉴의 자식 중 게시판이 있으면 SKIP(분리 필요, 수동).
대상: 랜딩행 >= 143(여성가족) 이고 그 prefix의 시트행이 1개(sheet=1)인 메뉴.
sheet>1(이미 G확장됨: 군민복지050307·하수처리050601·주민참여060603)은 SKIP.
"""
import sys, re, json, openpyxl
import urllib.request, ssl
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor

PATH = r'D:\01.프로젝트\DB수집\작업파일\광역_사이트맵\충청남도\3.금산군\충청남도_금산군.xlsx'
WRITE = '--write' in sys.argv
ctx = ssl.create_default_context(); ctx.check_hostname = False; ctx.verify_mode = ssl.CERT_NONE

def fetch(u):
    req = urllib.request.Request(u, headers={'User-Agent': 'Mozilla/5.0'})
    try:
        return urllib.request.urlopen(req, context=ctx, timeout=15).read().decode('utf-8', 'replace')
    except Exception:
        return None

def analyze(code):
    sub = 'sub06' if code.startswith('06') else 'sub05'
    h = fetch(f'https://www.geumsan.go.kr/kr/html/{sub}/{code}.html')
    if h is None:
        return None
    soup = BeautifulSoup(h, 'html.parser')
    t = soup.select_one('.location_wrap li:last-child, h2.h2')
    title = t.get_text(strip=True) if t else '?'
    ntab = len(soup.select('ul.ui-nav_tabs a.ui-tabs_link'))
    board = bool(soup.select('table.bbs,.board_list,.bbs_list,.pagination')) or bool(re.search(r'총\s*[\d,]+\s*건', str(soup)))
    return dict(code=code, title=title, ntab=ntab, board=board, leaf=max(1, ntab))

wb = openpyxl.load_workbook(PATH); ws = wb.active

# prefix -> sheet rows (sub05/sub06 8-digit)
pref_rows = {}
for r in range(3, ws.max_row + 1):
    k = ws.cell(r, 11).value
    if not k:
        continue
    m = re.search(r'/sub0[56]/(\d{6})(\d{2})\.html', str(k))
    if m:
        pref_rows.setdefault(m.group(1), []).append((r, m.group(0)))

targets = {}
for pfx, rows in pref_rows.items():
    landing = min(r for r, _ in rows)
    if landing < 143:
        continue          # 여성가족(143) 위는 제외
    if len(rows) != 1:
        print(f'SKIP {pfx} (sheet={len(rows)} rows={[r for r,_ in rows]}) — 이미 확장됨')
        continue
    targets[pfx] = landing

print(f'대상 sheet=1 메뉴: {len(targets)}')
plan = []
for pfx, landing in sorted(targets.items(), key=lambda x: x[1]):
    codes = [f'{pfx}{xx:02d}' for xx in range(1, 16)]
    kids = [k for k in ThreadPoolExecutor(8).map(analyze, codes) if k]
    leaf_sum = sum(k['leaf'] for k in kids)
    nboard = sum(1 for k in kids if k['board'])
    cur_m = ws.cell(landing, 13).value
    cur_f = ws.cell(landing, 6).value or ws.cell(landing, 7).value
    flag = ' ⚠GESIPAN' if nboard else ''
    plan.append(dict(pfx=pfx, row=landing, m_old=cur_m, m_new=leaf_sum, nkids=len(kids), nboard=nboard, kids=kids))
    print(f'row{landing} {pfx} [{cur_f}] M {cur_m}->{leaf_sum} (자식{len(kids)},board{nboard}){flag}')
    if nboard:
        for k in kids:
            print(f'       {k["code"]} leaf{k["leaf"]} board={k["board"]} {k["title"][:20]}')

# 적용: 게시판 없는 메뉴만 M 갱신
applied = 0
for p in plan:
    if p['nboard'] == 0:
        ws.cell(p['row'], 13).value = p['m_new']
        applied += 1
    else:
        print(f'  ⚠ row{p["row"]} {p["pfx"]} 게시판 포함 → M 미적용(수동 검토)')
print(f'적용 대상(게시판없음): {applied}/{len(plan)}')

json.dump([{k: v for k, v in p.items() if k != 'kids'} for p in plan],
          open(r'D:\01.프로젝트\DB수집\_temp\_geumsan_subtreeM_plan.json', 'w', encoding='utf8'), ensure_ascii=False)

if WRITE:
    wb.save(PATH); print('SAVED')
else:
    print('DRY-RUN (--write 로 저장)')
