# -*- coding: utf-8 -*-
"""kesco N 재판정 v2 — 본문(.contents) 이미지 '원본크기' 기준 수집 + 실제 이미지 다운로드 몽타주.
렌더 0x0(탭/지연로딩) 이미지도 원본크기로 포착. 영상도 탐지.
출력: _nshots2\ (이미지 다운로드 + montage_*.png) + plan2.json
"""
import sys, os, re, json, warnings, io
import openpyxl, requests
from PIL import Image, ImageDraw, ImageFont
from playwright.sync_api import sync_playwright

warnings.filterwarnings('ignore')
requests.packages.urllib3.disable_warnings()
HERE = os.path.dirname(os.path.abspath(__file__))
XLSX = os.path.join(HERE, '한국전기안전공사.xlsx')
OUT = os.path.join(HERE, '_nshots2')
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
# kesco는 콘텐츠 이미지를 /common/images/sub|office 에 둠 → 경로(/common)로 배제하면 안 됨. 파일명 기준 데코필터.
NAME_NOISE = re.compile(r'(^ico[_\-]|icon|logo|btn[_\-]|^bul|bullet|^bg[_\-]|banner|^sns|blank|spacer|loading|arrow|^dot|^line[_\.]|^top_|footer|^foot|^header|symbol|copyright|^qr|no_img|noimage|share|facebook|insta|twitter|naver|kakao|mark_)', re.I)
PATH_NOISE = re.compile(r'(/skin/|/template/|/editor/|/sample)', re.I)

JS = r"""()=>{
  const cont=document.querySelector('#contents,.contents,#content,.content,.sub_content,.sub_contents,#sub_content,#container')||document.body;
  const imgs=[];
  for(const i of cont.querySelectorAll('img')){
    imgs.push({src:i.currentSrc||i.src||'',alt:(i.alt||''),nw:i.naturalWidth,nh:i.naturalHeight});}
  let vid=0;
  if(cont.querySelector('video,source[src*=".mp4"]')) vid++;
  for(const f of cont.querySelectorAll('iframe')){const s=f.src||'';if(/youtube|youtu\.be|vimeo|player\./i.test(s))vid++;}
  return {imgs,vid};
}"""


def candidate_imgs(items):
    """원본크기 기준 실콘텐츠 이미지 후보. 마크/로고/아이콘 제외, 원본 양변 충분히 큼."""
    out = []
    seen = set()
    for it in items:
        src = it.get('src', '')
        if not src or src in seen:
            continue
        base = src.split('?')[0].split('/')[-1]
        if NAME_NOISE.search(base) or PATH_NOISE.search(src):
            continue
        nw, nh = it['nw'], it['nh']
        if nw >= 200 and nh >= 150 and (nw * nh) >= 40000:
            seen.add(src)
            out.append(it)
    return out


def run():
    wb = openpyxl.load_workbook(XLSX); ws = wb.active
    os.makedirs(OUT, exist_ok=True)
    targets = []
    for r in range(67, ws.max_row + 1):
        if ws.cell(r, 2).value is None:
            continue
        L = ws.cell(r, 12).value or ''
        url = ws.cell(r, 11).value
        if L == '사이트' or not (url and isinstance(url, str) and url.startswith('http')):
            continue
        targets.append((r, url, L))
    print(f'대상 {len(targets)}행', flush=True)
    plan = []
    dl = []  # (row, label, localpath)
    sess = requests.Session(); sess.headers['User-Agent'] = UA
    with sync_playwright() as p:
        b = p.chromium.launch()
        pg = b.new_page(user_agent=UA, viewport={'width': 1280, 'height': 2000})
        for idx, (r, url, L) in enumerate(targets):
            rec = {'row': r, 'url': url, 'L': L}
            try:
                pg.goto(url, timeout=25000, wait_until='domcontentloaded')
                pg.wait_for_timeout(1200)
                try:
                    pg.evaluate("()=>window.scrollTo(0,document.body.scrollHeight)")
                    pg.wait_for_timeout(900)
                    pg.evaluate("()=>window.scrollTo(0,0)")
                    pg.wait_for_timeout(300)
                except Exception:
                    pass
                d = pg.evaluate(JS)
                ci = candidate_imgs(d['imgs'])
                rec['vid'] = d['vid']
                rec['nimg'] = len(ci)
                rec['imgs'] = [{'src': it['src'], 'nw': it['nw'], 'nh': it['nh'], 'alt': it['alt'][:30]} for it in ci[:6]]
                rec['auto'] = 'candidate' if (ci or d['vid']) else '어문'
                # 대표 이미지 최대 3장 다운로드
                for it in ci[:3]:
                    s = it['src']
                    try:
                        rr = sess.get(s, timeout=12, verify=False)
                        im = Image.open(io.BytesIO(rr.content)).convert('RGB')
                        lp = os.path.join(OUT, f'r{r}_{len(dl)}.jpg')
                        im.thumbnail((360, 360)); im.save(lp, quality=80)
                        lab = ws.cell(r, 6).value or ws.cell(r, 5).value or ws.cell(r, 4).value or ''
                        dl.append((r, str(lab)[:18], lp))
                    except Exception:
                        pass
            except Exception as e:
                rec['auto'] = '어문'; rec['err'] = str(e)[:40]
            plan.append(rec)
            if (idx + 1) % 20 == 0:
                print(f'  {idx+1}/{len(targets)}', flush=True)
        b.close()
    # 몽타주 (실제 이미지)
    try: font = ImageFont.truetype('malgun.ttf', 16)
    except Exception: font = ImageFont.load_default()
    cols, cw, ch = 5, 250, 280; per = cols * 6
    mps = []
    for gi in range(0, len(dl), per):
        chunk = dl[gi:gi + per]
        rn = (len(chunk) + cols - 1) // cols
        cv = Image.new('RGB', (cols * cw, rn * ch), 'white'); dr = ImageDraw.Draw(cv)
        for j, (r, lab, lp) in enumerate(chunk):
            try:
                im = Image.open(lp).convert('RGB'); im.thumbnail((cw - 8, ch - 28))
            except Exception: continue
            cx, cy = (j % cols) * cw, (j // cols) * ch
            cv.paste(im, (cx + 4, cy + 24))
            dr.rectangle([cx, cy, cx + cw - 1, cy + ch - 1], outline='gray')
            dr.text((cx + 4, cy + 4), f'r{r} {lab}', fill='red', font=font)
        mp = os.path.join(OUT, f'montage_{gi//per+1}.png'); cv.save(mp); mps.append(mp)
    json.dump(plan, open(os.path.join(OUT, 'plan2.json'), 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
    cand = [x for x in plan if x['auto'] == 'candidate']
    vid = [x for x in plan if x.get('vid')]
    print(f'어문 {len(plan)-len(cand)} | 후보 {len(cand)} | 영상 {len(vid)} | 다운이미지 {len(dl)} | 몽타주 {len(mps)}', flush=True)
    for mp in mps: print('  MONT:', mp, flush=True)


if __name__ == '__main__':
    run()
