"""전북·제주 16개 시·군 사이트맵 URL 탐색 + 컨테이너 구조 덤프."""
import re
import ssl
import sys
import warnings
from urllib.parse import urljoin, urlparse

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.ssl_ import create_urllib3_context
from bs4 import BeautifulSoup

warnings.filterwarnings('ignore')
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
H = {'User-Agent': UA}


class WeakSSLAdapter(HTTPAdapter):
    def init_poolmanager(self, *args, **kwargs):
        ctx = create_urllib3_context()
        ctx.set_ciphers('DEFAULT@SECLEVEL=0')
        ctx.options |= 0x4
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        kwargs['ssl_context'] = ctx
        return super().init_poolmanager(*args, **kwargs)


def make_session(weak=False):
    s = requests.Session()
    s.headers.update(H)
    if weak:
        s.mount('https://', WeakSSLAdapter())
    return s


def fetch(session, url, timeout=20):
    r = session.get(url, timeout=timeout, verify=False, allow_redirects=True)
    meta = re.search(rb'<meta[^>]*charset=["\']?\s*([\w-]+)', r.content[:4096], re.I)
    if meta:
        r.encoding = meta.group(1).decode('ascii', errors='ignore')
    else:
        r.encoding = r.apparent_encoding
    return r


SITES = [
    ('고창군', 'https://www.gochang.go.kr/index.gochang?contentsSid=3136'),
    ('군산시', 'https://www.gunsan.go.kr/main'),
    ('김제시', 'https://www.gimje.go.kr/index.gimje'),
    ('남원시', 'https://www.namwon.go.kr/index.do?menuUid=ff8080818e3beff0018e40e8f63e02d2'),
    ('무주군', 'https://www.muju.go.kr/index.9is'),
    ('부안군', 'https://www.buan.go.kr/index.buan?contentsSid=1'),
    ('순창군', 'https://www.sunchang.go.kr/'),
    ('완주군', 'https://www.wanju.go.kr/index.9is'),
    ('익산시', 'https://www.iksan.go.kr/index.do?menuUid=ff8080819a39930e019a4de8c1ae0afd'),
    ('임실군', 'https://www.imsil.go.kr/index.imsil'),
    ('장수군', 'https://www.jangsu.go.kr/index.jangsu'),
    ('전주시', 'https://www.jeonju.go.kr/index.9is'),
    ('정읍시', 'https://www.jeongeup.go.kr/index.jeongeup'),
    ('진안군', 'https://www.jinan.go.kr/index.jinan?contentsSid=1379'),
    ('서귀포시', 'https://www.seogwipo.go.kr/index.htm'),
    ('제주시', 'https://www.jejusi.go.kr/index.ac'),
]


def find_sitemap_links(soup, base):
    """페이지에서 사이트맵으로 보이는 링크 후보 수집."""
    cands = []
    for a in soup.find_all('a', href=True):
        txt = (a.get_text() or '').strip()
        href = a['href']
        onclick = a.get('onclick', '')
        blob = f'{txt} {href} {onclick}'.lower()
        if '사이트맵' in txt or 'sitemap' in blob:
            url = href
            if url.startswith('#') or url.lower().startswith('javascript:'):
                # onclick에서 추출 시도
                m = re.search(r"""['"]([^'"]*(?:sitemap|site_map)[^'"]*)['"]""", onclick, re.I)
                if m:
                    url = m.group(1)
                else:
                    continue
            cands.append((txt, urljoin(base, url)))
    # 중복 제거
    seen = set(); out = []
    for t, u in cands:
        if u not in seen:
            seen.add(u); out.append((t, u))
    return out


def dump_structure(soup):
    """사이트맵 페이지 주요 컨테이너 후보 출력."""
    # 흔한 사이트맵 컨테이너 셀렉터 후보
    sels = [
        'div.sitemap', 'div#sitemap', 'div.site_map', 'div#site_map',
        'ul#menu_sitemap', 'div.depth.depth1', 'div.depth1',
        'ul.depth1_ul', 'div.amThum', 'ul.sitemap', 'div.sitemap_wrap',
        'div.contents_sitemap', 'div.sitemapWrap', 'div.site-map',
        'div.allmenu', 'div#allmenu', 'div.gnb_all', 'div.total_menu',
    ]
    found = []
    for sel in sels:
        els = soup.select(sel)
        if els:
            found.append((sel, len(els)))
    print(f'    매칭 셀렉터: {found}')
    # 사이트맵스러운 컨테이너 한 개 잡아서 자식 구조 덤프
    target = None
    for sel, _ in found:
        target = soup.select_one(sel)
        if target:
            print(f'    >>> {sel} 내부 구조:')
            break
    if not target:
        # body에서 class에 sitemap/menu/depth 포함 div 찾기
        for div in soup.find_all(['div', 'ul'], class_=True):
            cls = ' '.join(div.get('class', []))
            if re.search(r'sitemap|site_map|allmenu|depth1|total_menu', cls, re.I):
                target = div
                print(f'    >>> <{div.name} class="{cls}"> 내부 구조:')
                break
    if not target:
        print('    !! 사이트맵 컨테이너 미발견')
        return
    # 자식 1~3레벨 태그/클래스 요약
    def summarize(el, depth=0, maxdepth=4):
        if depth > maxdepth:
            return
        for child in el.find_all(recursive=False):
            cls = '.'.join(child.get('class', []))
            idv = child.get('id', '')
            tag = child.name
            label = tag + (f'#{idv}' if idv else '') + (f'.{cls}' if cls else '')
            a = child.find('a', recursive=False)
            atxt = (a.get_text().strip()[:20] if a else '')
            print('      ' * (depth + 1) + f'{label}' + (f'  a="{atxt}"' if atxt else ''))
            if depth < 2:
                summarize(child, depth + 1, maxdepth)
    summarize(target)


def main():
    targets = sys.argv[1:]
    for name, url in SITES:
        if targets and name not in targets:
            continue
        print(f'\n{"="*70}\n[{name}] {url}')
        for weak in (False, True):
            try:
                sess = make_session(weak=weak)
                r = fetch(sess, url)
                base = f'{urlparse(r.url).scheme}://{urlparse(r.url).netloc}'
                soup = BeautifulSoup(r.text, 'html.parser')
                print(f'  status={r.status_code} final={r.url} weak_ssl={weak}')
                links = find_sitemap_links(soup, base)
                print(f'  사이트맵 링크 후보: {links[:6]}')
                # 가장 그럴듯한 후보 따라가기
                if links:
                    smurl = links[0][1]
                    try:
                        r2 = fetch(sess, smurl)
                        soup2 = BeautifulSoup(r2.text, 'html.parser')
                        print(f'  사이트맵 페이지: {r2.url} (status {r2.status_code})')
                        dump_structure(soup2)
                    except Exception as e:
                        print(f'  사이트맵 페이지 fetch 실패: {e}')
                else:
                    print('  >>> 인덱스 자체 구조 확인:')
                    dump_structure(soup)
                break
            except Exception as e:
                if weak:
                    print(f'  !! 실패(weak 포함): {type(e).__name__}: {e}')
                else:
                    print(f'  (일반 SSL 실패 → weak 재시도): {type(e).__name__}')


if __name__ == '__main__':
    main()
