#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
爬虫：石家庄市行政审批局 - 公告公示
站点：xzspj.sjz.gov.cn
CMS：zyyCMS（UUID路径 + AJAX分页）
注意：列表页页码2+仅在浏览器（Browserbase）中加载，SSH服务器curl获取不到。
      Page 1可正常提取，pages参数决定获取前几页（仅page1可靠）。
"""

import sys, os, re, requests, time
from bs4 import BeautifulSoup

_HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, _HERE)
from crawler_lib import push_to_searchdb
import urllib.parse

SITE_NAME = "石家庄市行政审批局-公告公示"

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36'
}

BASE_URL = 'https://xzspj.sjz.gov.cn'
INDEX_URL = 'https://xzspj.sjz.gov.cn/columns/82dab922-bb4f-4f11-a919-4ccf0f650e6a/index.html'

session = requests.Session()
session.headers.update(HEADERS)


def safe_get(url, max_retries=3):
    for attempt in range(max_retries):
        try:
            r = session.get(url, timeout=30)
            r.encoding = 'utf-8'
            return r
        except (requests.ConnectionError, requests.Timeout) as e:
            if attempt < max_retries - 1:
                time.sleep(2)
                continue
            print(f"    ⚠️ 请求失败({url}): {e}")
            return None
    return None


def fetch_list_page(page):
    """获取列表页 - 注意：只有page=1有效（内嵌在HTML中），page2+需浏览器"""
    url = INDEX_URL if page == 1 else f"{INDEX_URL}?page={page}"
    r = safe_get(url)
    if r is None:
        return []

    soup = BeautifulSoup(r.text, 'html.parser')
    items = []

    # 从pagingCont容器中提取列表
    cont = soup.find('div', id=re.compile(r'pagingCont_'))
    if not cont:
        return []

    for nrlb in cont.find_all('div', class_='nrlb'):
        a_tag = nrlb.find('a', href=re.compile(r'/columns/'))
        if not a_tag:
            continue
        href = a_tag.get('href', '')
        title = a_tag.get('title', '') or a_tag.get_text(strip=True)
        if not title or not href:
            continue
        if href and not href.startswith('http'):
            href = BASE_URL + href

        # 日期
        date_div = nrlb.find('div', style=re.compile(r'float:\s*right'))
        date_str = date_div.get_text(strip=True) if date_div else ''

        items.append({'title': title, 'url': href, 'date': date_str})

    return items


def fetch_detail(url):
    """获取详情页正文 — 保留 <p>/<table>/<a> HTML（search_app 按 HTML 渲染）"""
    r = safe_get(url)
    if r is None:
        return ''

    soup = BeautifulSoup(r.text, 'html.parser')

    # 正文容器 - 先找 conN（内容区），再回退
    content_div = soup.find('div', id='conN')
    if not content_div:
        content_div = soup.find('div', class_=re.compile(r'nrzw|article|content|detail', re.I))
    if not content_div:
        content_div = soup.find('div', id=re.compile(r'zoom|article|content', re.I))
    if not content_div:
        return ''

    # 用 decode_contents 序列化保留 <p>/<table> 结构
    raw = content_div.decode_contents()

    # 1. 表格占位符法：先摘除 <table> 保存，避免被剥标签破坏
    tables = []
    def _hold_table(m):
        tables.append(m.group(0))
        return f'@@TABLE{len(tables)-1}@@'
    raw = re.sub(r'<table[^>]*>.*?</table>', _hold_table, raw, flags=re.S | re.I)

    # 2. 附件/链接先绝对化
    def _abs_link(m):
        href = m.group(1)
        txt = m.group(2)
        if href and href.startswith('/'):
            href = 'https://xzspj.sjz.gov.cn' + href
        return f'<a href="{href}">{txt}</a>'
    raw = re.sub(r'<a[^>]*href="([^"]*)"[^>]*>(.*?)</a>', _abs_link, raw, flags=re.S | re.I)

    # 3. 剥 script/style 和多余标签，保留 <p>/<br>
    raw = re.sub(r'<(script|style)[^>]*>.*?</\1>', '', raw, flags=re.S | re.I)
    # 规范化 <p> 标签（去内联样式）
    raw = re.sub(r'<p[^>]*>', '<p>', raw)
    raw = re.sub(r'</p>', '</p>', raw)
    # 去掉其他标签属性，保留标签名
    raw = re.sub(r'<(/?)(div|span|font|strong|b|em|i|u|h[1-6]|ul|ol|li|tbody|thead|tr|td|th|img|br|hr|section|article)([^>]*)>',
                 lambda m: f'<{m.group(1)}{m.group(2)}>', raw)

    # 4. 还原表格
    for i, t in enumerate(tables):
        # 表格内也规范化 <p>/<td> 标签
        t2 = re.sub(r'<p[^>]*>', '<p>', t)
        t2 = re.sub(r'<(/?)(div|span|font|strong|b|em|i|u)([^>]*)>', lambda m: f'<{m.group(1)}{m.group(2)}>', t2)
        raw = raw.replace(f'@@TABLE{i}@@', t2)

    # 4.5 纯文本段落 div → p（search_app 只认 <p>/<table> 走 HTML 渲染）
    # 无子块级元素的 div（文本段落）转 <p>
    def _div_to_p(m):
        inner = m.group(1).strip()
        # 只转不含块级标签的 div
        if re.search(r'<(table|div|p|ul|ol|h[1-6])', inner, re.I):
            return f'<div>{inner}</div>'
        return f'<p>{inner}</p>'
    # 多次迭代处理嵌套
    for _ in range(5):
        new_raw = re.sub(r'<div>([^<]*(?:<[^/][^>]*>[^<]*)*[^<]*)</div>', _div_to_p, raw, flags=re.S)
        if new_raw == raw:
            break
        raw = new_raw

    # 5. 清理纯空白段落
    def _clean_p(m):
        inner = m.group(1)
        text = re.sub(r'<[^>]+>', '', inner)
        text = re.sub(r'&nbsp;|\u3000|&#160;|\s', '', text)
        if not text and '<img' not in inner:
            return ''
        return f'<p>{inner.strip()}</p>'
    raw = re.sub(r'<p>(.*?)</p>', _clean_p, raw, flags=re.S)

    # 6. 多空行压缩
    raw = re.sub(r'\n{3,}', '\n\n', raw)
    raw = re.sub(r'<p>\s*</p>', '', raw)

    body = raw.strip()
    if not body:
        return ''
    return body


def _extract_nodes(node, parts):
    for child in node.children:
        if child.name == 'p':
            text = child.get_text(' ', strip=True)
            if text:
                parts.append(text)
        elif child.name == 'table':
            md = html_table_to_html(str(child))
            if md:
                parts.append(md)
        elif child.name in ('div', 'section', 'article'):
            # 纯文本div直接提取
            if child.name == 'div' and not child.find(['div', 'p', 'table']):
                text = child.get_text(' ', strip=True)
                if text:
                    parts.append(text)
            else:
                _extract_nodes(child, parts)


def html_table_to_html(table, base_url=""):
    """保留 HTML 表格结构，仅将相对链接/图片转绝对 URL"""
    from bs4 import BeautifulSoup
    tbl = BeautifulSoup(str(table), 'html.parser')
    for a in tbl.find_all('a'):
        href = a.get('href', '')
        if href and not href.startswith(('http', 'javascript', '#')):
            a['href'] = urllib.parse.urljoin(base_url, href) if base_url else href
    for img in tbl.find_all('img'):
        src = img.get('src', '')
        if src and not src.startswith(('http', '//', 'data:')):
            img['src'] = urllib.parse.urljoin(base_url, src) if base_url else src
    return str(tbl)


def main():
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--pages', type=int, default=5, help='尝试抓取前N页（注意：page2+需要浏览器环境）')
    args = parser.parse_args()
    max_pages = args.pages

    print(f"[{SITE_NAME}] 开始 (max_pages={max_pages})")

    all_items = []
    seen_urls = set()
    for page in range(1, max_pages + 1):
        items = fetch_list_page(page)
        if not items:
            if page == 1:
                print("  ⚠️ 第1页为空，退出")
                return
            print(f"  第{page}页: 空（page2+需浏览器环境）")
            break
        # 去重：page2+可能返回和page1相同的内容（AJAX不工作）
        new_items = [it for it in items if it['url'] not in seen_urls]
        for it in new_items:
            seen_urls.add(it['url'])
        if new_items:
            print(f"  第{page}页: {len(items)} 条（新{len(new_items)}条）")
            all_items.extend(new_items)
        else:
            print(f"  第{page}页: {len(items)} 条（全部重复，结束）")

    print(f"\n列表合计: {len(all_items)} 条")

    if not all_items:
        print("  列表为空，退出")
        return

    db_items = []
    success = 0
    failed = 0
    for i, item in enumerate(all_items):
        url = item['url']
        title = item['title']
        date_str = item['date']
        print(f"  [{i+1}/{len(all_items)}] {title[:40]}...", end=' ')
        body = fetch_detail(url)
        if not body:
            print("⚠️ 空正文")
            failed += 1
        else:
            print(f"✅ {len(body)}字")
            success += 1

        db_items.append({
            'site_name': SITE_NAME,
            'source_url': url,
            'url': url,
            'title': title,
            'pub_date': date_str,
            'summary': body[:500] if body else '',
            'content': body,
        })

    push_to_searchdb(db_items, batch_label=SITE_NAME)
    print(f"\n[{SITE_NAME}] 完成: 共{len(all_items)}条, 正文成功{success}, 空{failed}")


if __name__ == '__main__':
    main()
