#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""数据层修复转换器 v3：处理 v2 遗漏的 152 条残留
v1 漏 :--- 格式（LIKE 过滤）→ v2 修复
v3 新增：
  1. 裸管道表格（无行首|）: "机构名称 | 江西云智" + "--- | ---"
  2. 多行单元格: "| 序号 | 项目\\n      名称 |"（行不以|结尾则吞并后续行）
  3. 空表头残留: 转换后残留 "|  |  |" 空行 + "| --- |"
识别策略：先找分隔行（含 --- 的行），锚定表格块 → 向上取表头 → 向下取数据行。
"""
import sqlite3, re, sys

MD_LINK_RE = re.compile(r'\[([^\]]{1,200})\]\((https?://[^)\s]+)\)')
MD_IMG_RE = re.compile(r'!\[([^\]]*)\]\((https?://[^)\s]+)\)')

# 分隔行：允许有/无首尾竖线，允许 :--- 对齐
SEP_RE = re.compile(r'^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$')
# 表格数据行（有|开头 或 裸管道含至少一个|）
PIPE_LINE_RE = re.compile(r'^\s*\|.*\|\s*$')
BARE_PIPE_RE = re.compile(r'^[^|\n]+\s*\|.*(\|.*)*$')


def split_cells(line):
    """分割表格行单元格（容忍单元格内 | 被转义的情况少，直接 split）"""
    s = line.strip()
    if s.startswith('|'):
        s = s[1:]
    if s.endswith('|'):
        s = s[:-1]
    return [c.strip() for c in s.split('|')]


def is_empty_cells(line):
    """全空单元格管道行: |  |  | → True"""
    s = line.strip()
    if not (s.startswith('|') and s.endswith('|')):
        return False
    cells = split_cells(s)
    return all(not c for c in cells)


def collect_table_row(lines, idx):
    """收集一行表格数据（含多行单元格吞并），返回 (merged_line, next_idx)"""
    cur = lines[idx].rstrip()
    if cur.lstrip().startswith('|'):
        # 以 | 开头: 若不以 | 结尾则吞并后续缩进续行直到完整
        merged = cur
        k = idx
        while not merged.rstrip().endswith('|') and k + 1 < len(lines):
            nxt = lines[k + 1].rstrip()
            if not nxt.strip() or SEP_RE.match(nxt):
                break
            merged += '\n' + nxt
            k += 1
        return merged, k + 1
    elif BARE_PIPE_RE.match(lines[idx]):
        # 裸管道: 吞并后续缩进续行（多行单元格，续行必须带前导空白）
        merged = cur
        k = idx
        while k + 1 < len(lines):
            nxt = lines[k + 1]
            if not nxt.strip() or SEP_RE.match(nxt.rstrip()):
                break
            # 下一行以|开头 或 是独立裸管道行 → 不吞
            if nxt.lstrip().startswith('|') or BARE_PIPE_RE.match(nxt):
                break
            # 只有带前导空白的缩进续行才吞（防吞普通正文行）
            if not re.match(r'^\s+\S', nxt):
                break
            merged += '\n' + nxt.rstrip()
            k += 1
        return merged, k + 1
    return None, idx


def is_table_row(line):
    """判断合并后的表格行：以|开头(去空白)且以|结尾，或裸管道（多行看首行）"""
    s = line.strip()
    if s.startswith('|') and s.endswith('|'):
        return True
    first = line.split('\n')[0]
    if BARE_PIPE_RE.match(first):
        return True
    return False


def md_table_to_html(text):
    lines = text.split('\n')
    # 预处理: 删除全空单元格管道行（|  |  | 残留表头噪音）
    lines = [ln for ln in lines if not is_empty_cells(ln)]
    out = []
    i = 0
    n = len(lines)
    while i < n:
        line = lines[i]
        if SEP_RE.match(line):
            # 找到分隔行 → 向上找表头，向下找数据行
            hdr = []
            j = i - 1
            while j >= 0 and lines[j].strip() and not SEP_RE.match(lines[j]):
                if is_empty_cells(lines[j]):
                    j -= 1
                    continue
                # 从表头块最顶行开始 collect（避免逐行收集重复）
                start = j
                while start - 1 >= 0 and lines[start - 1].strip() and not SEP_RE.match(lines[start - 1]) and \
                        not is_empty_cells(lines[start - 1]) and \
                        ('|' in lines[start - 1]):
                    start -= 1
                r, _ = collect_table_row(lines, start)
                if r is not None and is_table_row(r):
                    hdr.insert(0, r)
                break
            # 数据行：紧邻下方，连续表格行
            data = []
            k = i + 1
            while k < n:
                cur = lines[k]
                if not cur.strip():
                    break
                if SEP_RE.match(cur):
                    break
                if is_empty_cells(cur):
                    k += 1
                    continue
                r, nk = collect_table_row(lines, k)
                if r is not None and is_table_row(r):
                    data.append(r)
                    k = nk
                else:
                    break
            all_rows = hdr + data
            # 保护: 必须有表头 + 表头列数与分隔行列数一致（防正文管道符+---行误判）
            sep_cells = len([c for c in split_cells(line) if c])
            hdr_cells = len([c for c in split_cells(hdr[0]) if c]) if hdr else 0
            # 检测: md 块后紧跟 <table 标签（Word 导出 md 残渣+HTML 重复）→ 删除 md 块
            nxt = k
            while nxt < n and not lines[nxt].strip():
                nxt += 1
            followed_by_html_table = nxt < n and lines[nxt].lstrip().startswith('<table')
            if followed_by_html_table:
                i = k
                continue
            if len(all_rows) >= 1 and len(hdr) >= 1 and sep_cells == hdr_cells:
                parsed = []
                for r in all_rows:
                    cells = split_cells(r)
                    if all(re.match(r'^:?-{2,}:?$', c) for c in cells if c):
                        continue
                    parsed.append(cells)
                # 列数一致性校验: 数据行列数 == 表头列数 的行占比 > 50% 才转（防正文误判）
                if parsed:
                    data_rows = parsed[1:] if hdr else parsed
                    if not data_rows:
                        out.append(line)
                        i += 1
                        continue
                    col_ok = sum(1 for cells in data_rows if len(cells) == sep_cells)
                    if col_ok < max(1, len(data_rows) * 0.5):
                        out.append(line)
                        i += 1
                        continue
                if parsed:
                    trs = []
                    for cells in parsed:
                        tds = ''.join(f'<td>{md_inline_to_html(c)}</td>' for c in cells)
                        trs.append(f'<tr>{tds}</tr>')
                    tbl = '<table><tbody>' + ''.join(trs) + '</tbody></table>'
                    # 回退 out 中已 append 的表头原始行（多行单元格按行数回退）
                    hdr_lines = sum(1 + h.count('\n') for h in hdr) if hdr else 0
                    if hdr_lines and len(out) >= hdr_lines:
                        # 确认 out 尾部就是表头行（简单校验：尾部行含管道）
                        tail_ok = all('|' in ln for ln in out[-hdr_lines:])
                        if tail_ok:
                            del out[-hdr_lines:]
                    out.append(tbl)
                    i = k
                    continue
        out.append(line)
        i += 1
    return '\n'.join(out)


def md_inline_to_html(cell):
    cell = MD_IMG_RE.sub(lambda m: f'<img src="{m.group(2)}" alt="{m.group(1)}">', cell)
    cell = MD_LINK_RE.sub(lambda m: f'<a href="{m.group(2)}">{m.group(1)}</a>', cell)
    cell = re.sub(r'\*\*([^*]+)\*\*', r'<strong>\1</strong>', cell)
    return cell


def content_to_html(content):
    # 保护: 无 md 表格分隔行（--- |）的 content 直接返回，不进入转换（防纯 HTML 存储误伤）
    if not re.search(r'^\s*\|?\s*:?-{2,}:?\s*(\|\s*:?-{2,}:?\s*)+\|?\s*$', content, re.M):
        return content
    content = md_table_to_html(content)
    content = MD_IMG_RE.sub(lambda m: f'<img src="{m.group(2)}" alt="{m.group(1)}">', content)
    lines = content.split('\n')
    new_lines = []
    for line in lines:
        m = re.match(r'^\s*\[([^\]]{1,200})\]\((https?://[^)\s]+)\)\s*$', line)
        if m:
            new_lines.append(f'<p><a href="{m.group(2)}">{m.group(1)}</a></p>')
        else:
            new_lines.append(MD_LINK_RE.sub(lambda m2: f'<a href="{m2.group(2)}">{m2.group(1)}</a>', line))
    return '\n'.join(new_lines)


def main():
    dry = '--dry' in sys.argv
    conn = sqlite3.connect("/root/search.db", timeout=60)
    conn.execute("PRAGMA busy_timeout=60000")
    conn.execute("PRAGMA journal_mode=WAL")
    c = conn.cursor()

    c.execute("SELECT COUNT(*) FROM gov_raw")
    total = c.fetchone()[0]
    print(f"总行数: {total}", flush=True)

    updates = []
    errors = 0
    c.execute("SELECT rowid, site_name, content, summary FROM gov_raw")
    while True:
        rows = c.fetchmany(10000)
        if not rows:
            break
        for rowid, site, content, summary in rows:
            if content is None:
                continue
            if not ('|' in content or '](http' in content or '![http' in content):
                continue
            try:
                new_content = content_to_html(content)
                new_summary = summary
                if summary and '](http' in summary:
                    new_summary = MD_LINK_RE.sub(
                        lambda m: f'<a href="{m.group(2)}">{m.group(1)}</a>', summary)
                if new_content != content or new_summary != summary:
                    updates.append((new_content, new_summary, rowid))
            except Exception as e:
                errors += 1
                print(f"  ❌ rowid {rowid}: {e}", flush=True)
        print(f"  已扫描, 收集 {len(updates)} 条...", flush=True)
    print(f"收集完成: {len(updates)} 条待更新 / 错误 {errors}", flush=True)

    if not dry:
        conn.execute("BEGIN")
        for i in range(0, len(updates), 5000):
            batch = updates[i:i + 5000]
            c.executemany("UPDATE gov_raw SET content=?, summary=? WHERE rowid=?", batch)
            conn.commit()
            print(f"  已写入 {i + len(batch)} ...", flush=True)
    conn.close()
    print(f"\n=== 完成 === (dry={dry}) 更新: {len(updates)}")


if __name__ == '__main__':
    main()
