"""
Word 文档导出模块
"""
import os
import re
import sqlite3
import logging
from datetime import datetime
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

import config

logger = logging.getLogger(__name__)

LEVEL_STYLES = {
    1: ('Heading 1', 16, True),
    2: ('Heading 2', 14, True),
    3: ('Heading 3', 13, False),
    4: ('Heading 4', 12, False),
}


def export_to_word(db_path: str, project_id: int) -> str:
    """
    生成 Word 文档并保存到 data/exports/，返回文件名。
    """
    conn = sqlite3.connect(db_path)
    try:
        # 获取项目信息
        cur = conn.cursor()
        cur.execute("SELECT name FROM projects WHERE id=?", (project_id,))
        project = cur.fetchone()
        if not project:
            raise ValueError(f'项目 {project_id} 不存在')
        project_name = project[0]

        # 获取标书大纲文本（用于标题页）
        cur.execute("SELECT outline FROM tender_data WHERE project_id=?", (project_id,))
        td = cur.fetchone()
        bid_title = project_name + '技术标书'
        if td and td[0]:
            first_line = td[0].strip().split('\n')[0].strip()
            if first_line:
                bid_title = first_line

        # 获取所有章节（按顺序）
        cur.execute('''
            SELECT section_number, section_title, level, is_leaf, content, intro_content
            FROM bid_sections
            WHERE project_id=?
            ORDER BY order_index
        ''', (project_id,))
        sections = cur.fetchall()

        doc = _build_document(bid_title, sections)

        # 保存文件
        os.makedirs(config.EXPORT_DIR, exist_ok=True)
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        safe_name = ''.join(c for c in project_name if c.isalnum() or c in '._- \u4e00-\u9fff')
        filename = f'{safe_name}_{timestamp}.docx'
        filepath = os.path.join(config.EXPORT_DIR, filename)
        doc.save(filepath)
        logger.info(f'导出完成: {filepath}')
        return filename

    finally:
        conn.close()


DISCLAIMER_TEXT = """\
免责声明

本工具仅供学习交流免费使用，所生成的技术方案不可直接用于投标，请务必人工核对。本工具不会通过任何平台进行销售，请用户注意辨别真伪。在您开始使用本AI标书制作服务之前，请认真阅读并同意以下关键条款。一旦您继续使用，即表示您已充分理解并认可本提示的全部内容。

服务定位
本工具为单机使用的AI标书辅助工具，旨在帮助您生成标书的参考素材。您需对最终自己编写的标书文件承担全部责任，包括审核、修改内容，确保其符合相关法律法规及项目要求。

准确性免责
本人不对AI生成内容的完全准确性与完整性作任何保证。您有义务自行核实所有关键信息，并自行承担因使用本工具所引发的一切后果。

标书风险
本工具所生成的素材文件仅作参考。若您使用（包括引用、修改或二次创作），需自行承担由此可能导致的废标、侵权等全部风险与责任，本人不承担任何相关责任。

责任限制
任何情形下，本人均不对因使用本服务而造成的任何直接、间接或衍生损失（例如利润损失、业务中断、数据丢失等）承担法律责任。

其他事项
本人保留随时修改或终止本服务的权利。本提示的解释及争议解决，均适用中华人民共和国法律。\
"""


def _add_disclaimer_page(doc: Document) -> None:
    """在文档开头插入免责声明页"""
    # 标题
    title_para = doc.add_paragraph()
    title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    title_run = title_para.add_run('免责声明')
    title_run.font.size = Pt(16)
    title_run.font.bold = True
    title_run.font.name = '黑体'
    title_run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')

    doc.add_paragraph()

    # 正文各段（跳过第一行标题，已单独渲染）
    body_lines = DISCLAIMER_TEXT.split('\n')[2:]  # 跳过"免责声明"和空行
    for line in body_lines:
        p = doc.add_paragraph()
        stripped = line.strip()
        # 小标题行（非空且后面没有缩进，即段落标题）
        is_section_title = bool(stripped) and not line.startswith(' ') and not line.startswith('\u3000')
        run = p.add_run(stripped if stripped else '')
        if is_section_title and stripped:
            run.font.bold = True
            run.font.size = Pt(11)
        else:
            run.font.size = Pt(10.5)
        run.font.name = 'Times New Roman'
        run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
        p.paragraph_format.space_after = Pt(4)
        _set_line_spacing_15(p)

    doc.add_page_break()


def _build_document(bid_title: str, sections) -> Document:
    doc = Document()

    # ── 页面设置 ─────────────────────────────────────────────────────────
    section_obj = doc.sections[0]
    section_obj.page_width = Cm(21)
    section_obj.page_height = Cm(29.7)
    section_obj.left_margin = Cm(3)
    section_obj.right_margin = Cm(2.5)
    section_obj.top_margin = Cm(2.5)
    section_obj.bottom_margin = Cm(2.5)

    # ── 免责声明页（第一页）─────────────────────────────────────────────
    _add_disclaimer_page(doc)

    # ── 标题页 ──────────────────────────────────────────────────────────
    title_para = doc.add_paragraph()
    title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    title_run = title_para.add_run(bid_title)
    title_run.font.size = Pt(22)
    title_run.font.bold = True
    title_run.font.color.rgb = RGBColor(0x1a, 0x56, 0xdb)
    title_run.font.name = '黑体'
    title_run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体')

    doc.add_paragraph()

    date_para = doc.add_paragraph()
    date_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    date_run = date_para.add_run(datetime.now().strftime('%Y年%m月'))
    date_run.font.size = Pt(14)
    date_run.font.name = '宋体'
    date_run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')

    doc.add_page_break()

    # ── 章节内容 ─────────────────────────────────────────────────────────
    for row in sections:
        _, title, level, is_leaf, content, intro = row
        level = min(int(level), 4)

        # 添加标题
        heading_text = title
        heading = doc.add_heading(level=level)
        heading.clear()
        run = heading.add_run(heading_text)
        _, font_size, bold = LEVEL_STYLES.get(level, ('Heading 4', 12, False))
        run.font.size = Pt(font_size)
        run.font.bold = bold
        run.font.name = '黑体' if level <= 2 else '宋体'
        run._element.rPr.rFonts.set(qn('w:eastAsia'), '黑体' if level <= 2 else '宋体')

        # 章节引言（非叶节点）
        if intro and intro.strip():
            _add_body_paragraphs(doc, intro)

        # 正文内容（叶节点）
        if content and content.strip():
            _add_body_paragraphs(doc, content)

    return doc


def _set_line_spacing_15(paragraph):
    """将段落设为 1.5 倍行距（Word 中的 WD_LINE_SPACING.MULTIPLE × 1.5）"""
    from docx.oxml.ns import qn as _qn
    pPr = paragraph._element.get_or_add_pPr()
    spacing = pPr.find(_qn('w:spacing'))
    if spacing is None:
        spacing = OxmlElement('w:spacing')
        pPr.append(spacing)
    spacing.set(_qn('w:line'), '360')       # 240 × 1.5 = 360 twips
    spacing.set(_qn('w:lineRule'), 'auto')


# ── 图/表标记解析 ─────────────────────────────────────────────────────────

_BLOCK_PATTERN = re.compile(
    r'\[FIGURE:([^\]]+)\](.*?)\[/FIGURE\]'
    r'|\[TABLE:([^\]]+)\](.*?)\[/TABLE\]',
    re.DOTALL
)


def _split_content_blocks(text: str) -> list:
    """
    将章节正文拆分为有序内容块列表：
      {'type': 'text',   'content': '...'}
      {'type': 'figure', 'title': '...', 'content': '...'}
      {'type': 'table',  'title': '...', 'content': '...'}
    """
    blocks = []
    last = 0
    for m in _BLOCK_PATTERN.finditer(text):
        if m.start() > last:
            blocks.append({'type': 'text', 'content': text[last:m.start()]})
        if m.group(1) is not None:
            blocks.append({'type': 'figure',
                           'title': m.group(1).strip(),
                           'content': m.group(2).strip()})
        else:
            blocks.append({'type': 'table',
                           'title': m.group(3).strip(),
                           'content': m.group(4).strip()})
        last = m.end()
    if last < len(text):
        blocks.append({'type': 'text', 'content': text[last:]})
    return blocks


def _set_para_shading(para, hex_fill: str):
    """为段落设置背景填充色"""
    pPr = para._element.get_or_add_pPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), hex_fill)
    pPr.append(shd)


def _set_cell_bg(cell, hex_fill: str):
    """为表格单元格设置背景色"""
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    shd.set(qn('w:fill'), hex_fill)
    tcPr.append(shd)


def _set_cell_padding(cell, pt_value: float):
    """设置表格单元格四侧内边距（单位：磅）"""
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcMar = OxmlElement('w:tcMar')
    val = str(int(pt_value * 20))   # pt → twips（1pt = 20 twips）
    for side in ('top', 'left', 'bottom', 'right'):
        node = OxmlElement(f'w:{side}')
        node.set(qn('w:w'), val)
        node.set(qn('w:type'), 'dxa')
        tcMar.append(node)
    tcPr.append(tcMar)


def _safe_set_eastasia(run, font_name: str):
    """安全设置东亚字体，确保 rPr 已存在"""
    _ = run.font.size  # 触发 rPr 创建
    try:
        run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
    except Exception:
        pass


def _add_block_caption(doc: Document, prefix: str, title: str):
    """添加图/表居中加粗标题行"""
    cap = doc.add_paragraph()
    cap.alignment = WD_ALIGN_PARAGRAPH.CENTER
    cap.paragraph_format.space_before = Pt(8)
    cap.paragraph_format.space_after = Pt(3)
    run = cap.add_run(f'{prefix}：{title}')
    run.font.bold = True
    run.font.size = Pt(11)
    run.font.name = 'Times New Roman'
    _safe_set_eastasia(run, '黑体')


def _add_figure_block(doc: Document, title: str, content: str):
    """
    将图示内容渲染为带边框 + 背景色的文字图示框。
    使用单格表格（Table Grid 样式）实现四周边框，比纯段落背景更专业。
    """
    _add_block_caption(doc, '图', title)

    lines = content.split('\n')

    # 单格表格：四周边框 + 淡蓝灰背景
    tbl = doc.add_table(rows=1, cols=1)
    tbl.style = 'Table Grid'
    cell = tbl.cell(0, 0)
    _set_cell_bg(cell, 'EFF3FB')        # 淡蓝灰背景
    _set_cell_padding(cell, 5)           # 内边距 5pt

    for i, line in enumerate(lines):
        if i == 0:
            para = cell.paragraphs[0]
            para.clear()
        else:
            para = cell.add_paragraph()
        para.paragraph_format.space_before = Pt(0)
        para.paragraph_format.space_after  = Pt(1)
        run = para.add_run(line if line else ' ')
        run.font.size = Pt(9.5)
        run.font.name = 'Courier New'
        _safe_set_eastasia(run, '宋体')

    # 图示后空行
    sp = doc.add_paragraph()
    sp.paragraph_format.space_after = Pt(8)


def _add_word_table(doc: Document, title: str, content: str):
    """将 Markdown 表格解析并渲染为 Word 表格"""
    # 解析 markdown 行，过滤掉分隔行（|---|）
    raw_rows = []
    for line in content.strip().split('\n'):
        line = line.strip()
        if not line:
            continue
        if re.match(r'^\|[\s\-:| ]+\|$', line):
            continue  # 分隔行
        if line.startswith('|') and line.endswith('|'):
            cells = [c.strip() for c in line[1:-1].split('|')]
            raw_rows.append(cells)

    if not raw_rows:
        # 没有解析到有效行时，降级为普通文本
        _add_block_caption(doc, '表', title)
        _add_plain_text(doc, content)
        return

    col_count = max(len(r) for r in raw_rows)
    rows = [r + [''] * (col_count - len(r)) for r in raw_rows]

    _add_block_caption(doc, '表', title)

    table = doc.add_table(rows=len(rows), cols=col_count)
    table.style = 'Table Grid'

    for i, row_data in enumerate(rows):
        for j, cell_text in enumerate(row_data):
            cell = table.cell(i, j)
            para = cell.paragraphs[0]
            para.clear()
            para.alignment = WD_ALIGN_PARAGRAPH.CENTER if i == 0 else WD_ALIGN_PARAGRAPH.LEFT
            run = para.add_run(cell_text)
            run.font.size = Pt(10)
            run.font.bold = (i == 0)
            run.font.name = 'Times New Roman'
            _safe_set_eastasia(run, '宋体')
            if i == 0:
                _set_cell_bg(cell, 'D6E4F7')  # 浅蓝表头

    # 表格后空行
    sp = doc.add_paragraph()
    sp.paragraph_format.space_after = Pt(6)


def _add_plain_text(doc: Document, text: str):
    """添加普通文本段落（内部辅助）"""
    for line in text.split('\n'):
        line = line.strip()
        if not line:
            continue
        p = doc.add_paragraph()
        p.paragraph_format.first_line_indent = Pt(24)
        p.paragraph_format.space_after = Pt(6)
        _set_line_spacing_15(p)
        run = p.add_run(line)
        run.font.size = Pt(12)
        run.font.name = 'Times New Roman'
        _safe_set_eastasia(run, '宋体')


def _add_body_paragraphs(doc: Document, text: str):
    """
    将正文文本分段渲染，自动识别并处理图示 [FIGURE:...] 和表格 [TABLE:...] 标记。
    """
    for block in _split_content_blocks(text):
        if block['type'] == 'figure':
            _add_figure_block(doc, block['title'], block['content'])
        elif block['type'] == 'table':
            _add_word_table(doc, block['title'], block['content'])
        else:
            _add_plain_text(doc, block['content'])


