"""
文档服务 - 处理 Word 文档导出相关功能
"""
import re
import logging
from urllib.parse import quote

logger = logging.getLogger(__name__)


def process_markdown_to_docx(doc, content):
    """
    将 Markdown 内容转换为格式化的 Word 文档元素
    
    支持的功能:
    - 表格 (Markdown 管道表格)
    - 标题 (# ## ###)
    - 粗体 (**text**)
    - 斜体 (*text* 或 _text_)
    - 粗斜体 (***text***)
    - 行内代码 (`code`)
    - 代码块 (```code```)
    - 删除线 (~~text~~)
    - 链接 ([text](url))
    - 无序列表 (- 或 *)
    - 有序列表 (1. 2. 3.)
    - 水平线 (--- 或 ***)
    """
    from docx.shared import RGBColor, Pt
    from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
    from docx.oxml.ns import qn

    def ensure_unicode_font(run, text):
        """确保 run 使用支持文本字符的字体"""
        try:
            text.encode('ascii')
            # 纯 ASCII 文本,无需特殊字体
        except UnicodeEncodeError:
            # 包含非 ASCII 字符,使用支持 Unicode 的字体
            run.font.name = 'Arial'
            # 设置东亚字体以支持中日韩字符
            r = run._element
            r.rPr.rFonts.set(qn('w:eastAsia'), 'Arial')
        return run

    def add_formatted_run(paragraph, text):
        """向段落添加带有行内格式的 run"""
        if not text:
            return

        # 行内格式匹配模式(顺序很重要:先检查三个星号,再检查两个/一个)
        patterns = [
            (r'\*\*\*(.*?)\*\*\*', lambda p, t: (lambda r: (setattr(r, 'bold', True), setattr(r, 'italic', True), ensure_unicode_font(r, t)))(p.add_run(t))),  # 粗斜体
            (r'\*\*(.*?)\*\*', lambda p, t: (lambda r: (setattr(r, 'bold', True), ensure_unicode_font(r, t)))(p.add_run(t))),  # 粗体
            (r'(?<!\*)\*(?!\*)(.*?)\*(?!\*)', lambda p, t: (lambda r: (setattr(r, 'italic', True), ensure_unicode_font(r, t)))(p.add_run(t))),  # 斜体 *
            (r'\b_(.*?)_\b', lambda p, t: (lambda r: (setattr(r, 'italic', True), ensure_unicode_font(r, t)))(p.add_run(t))),  # 斜体 _
            (r'~~(.*?)~~', lambda p, t: (lambda r: (setattr(r, 'strike', True), ensure_unicode_font(r, t)))(p.add_run(t))),  # 删除线
            (r'`([^`]+)`', lambda p, t: add_code_run(p, t)),  # 行内代码
            (r'\[([^\]]+)\]\(([^)]+)\)', lambda p, t, u: add_link_run(p, t, u)),  # 链接
        ]

        def add_code_run(para, text):
            """添加等宽字体的行内代码"""
            run = para.add_run(text)
            run.font.name = 'Courier New'
            run.font.size = Pt(10)
            run.font.color.rgb = RGBColor(220, 20, 60)  # 深红色
            try:
                text.encode('ascii')
            except UnicodeEncodeError:
                r = run._element
                r.rPr.rFonts.set(qn('w:eastAsia'), 'Consolas')
            return run

        def add_link_run(para, text, url):
            """添加超链接样式的 run"""
            full_text = f"{text} ({url})"
            run = para.add_run(full_text)
            run.font.color.rgb = RGBColor(0, 0, 255)  # 蓝色
            run.font.underline = True
            ensure_unicode_font(run, full_text)
            return run

        # 处理文本中的所有格式模式
        remaining_text = text
        while remaining_text:
            earliest_match = None
            earliest_pos = len(remaining_text)
            matched_pattern = None

            # 查找最早匹配的模式
            for pattern, handler in patterns:
                match = re.search(pattern, remaining_text)
                if match and match.start() < earliest_pos:
                    earliest_match = match
                    earliest_pos = match.start()
                    matched_pattern = handler

            if earliest_match:
                # 添加匹配前的普通文本
                if earliest_pos > 0:
                    run = paragraph.add_run(remaining_text[:earliest_pos])
                    ensure_unicode_font(run, remaining_text[:earliest_pos])

                # 应用匹配到的格式
                if '[' in earliest_match.group(0) and '](' in earliest_match.group(0):
                    # 链接特殊处理(两个分组)
                    matched_pattern(paragraph, earliest_match.group(1), earliest_match.group(2))
                else:
                    matched_pattern(paragraph, earliest_match.group(1))

                # 继续处理剩余文本
                remaining_text = remaining_text[earliest_match.end():]
            else:
                # 没有更多模式,添加剩余文本
                run = paragraph.add_run(remaining_text)
                ensure_unicode_font(run, remaining_text)
                break

    def parse_table(lines, start_idx):
        """解析从指定索引开始的 Markdown 表格"""
        if start_idx >= len(lines):
            return None, start_idx

        if '|' not in lines[start_idx]:
            return None, start_idx

        table_data = []
        idx = start_idx

        while idx < len(lines) and '|' in lines[idx]:
            # 跳过分隔线
            if re.match(r'^[\s\|\-:]+$', lines[idx]):
                idx += 1
                continue

            # 解析单元格
            cells = [cell.strip() for cell in lines[idx].split('|')]
            if cells and not cells[0]:
                cells = cells[1:]
            if cells and not cells[-1]:
                cells = cells[:-1]

            if cells:
                table_data.append(cells)
            idx += 1

        if table_data:
            return table_data, idx
        return None, start_idx

    # 按行分割内容
    lines = content.split('\n')
    i = 0
    in_code_block = False
    code_block_content = []

    while i < len(lines):
        line = lines[i]

        # 处理代码块
        if line.strip().startswith('```'):
            if not in_code_block:
                in_code_block = True
                code_block_content = []
            else:
                # 代码块结束 - 添加为预格式化文本
                in_code_block = False
                if code_block_content:
                    p = doc.add_paragraph()
                    p.style = 'Normal'
                    code_text = '\n'.join(code_block_content)
                    run = p.add_run(code_text)
                    run.font.name = 'Courier New'
                    run.font.size = Pt(10)
                    run.font.color.rgb = RGBColor(64, 64, 64)
                    try:
                        code_text.encode('ascii')
                    except UnicodeEncodeError:
                        r = run._element
                        r.rPr.rFonts.set(qn('w:eastAsia'), 'Consolas')
            i += 1
            continue

        if in_code_block:
            code_block_content.append(line)
            i += 1
            continue

        # 检查表格
        table_data, end_idx = parse_table(lines, i)
        if table_data:
            # 创建 Word 表格
            table = doc.add_table(rows=len(table_data), cols=len(table_data[0]))
            table.style = 'Table Grid'

            # 填充表格
            for row_idx, row_data in enumerate(table_data):
                for col_idx, cell_text in enumerate(row_data):
                    if col_idx < len(table.rows[row_idx].cells):
                        cell = table.rows[row_idx].cells[col_idx]
                        cell.text = ""
                        p = cell.add_paragraph()
                        add_formatted_run(p, cell_text)
                        # 表头行加粗
                        if row_idx == 0:
                            for run in p.runs:
                                run.bold = True

            doc.add_paragraph('')  # 表格后添加空行
            i = end_idx
            continue

        line = line.rstrip()

        # 跳过空行
        if not line:
            doc.add_paragraph('')
            i += 1
            continue

        # 水平线
        if re.match(r'^(\*{3,}|-{3,}|_{3,})$', line.strip()):
            p = doc.add_paragraph('─' * 50)
            p.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            i += 1
            continue

        # 标题
        if line.startswith('# '):
            doc.add_heading(line[2:], 1)
        elif line.startswith('## '):
            doc.add_heading(line[3:], 2)
        elif line.startswith('### '):
            doc.add_heading(line[4:], 3)
        elif line.startswith('#### '):
            doc.add_heading(line[5:], 4)
        # 无序列表
        elif line.lstrip().startswith('- ') or line.lstrip().startswith('* '):
            indent = len(line) - len(line.lstrip())
            bullet_text = line.lstrip()[2:]
            p = doc.add_paragraph(style='List Bullet')
            if indent > 0:
                p.paragraph_format.left_indent = Pt(indent * 10)
            add_formatted_run(p, bullet_text)
        # 有序列表
        elif re.match(r'^\s*\d+\.', line):
            match = re.match(r'^(\s*)(\d+)\.\s*(.*)', line)
            if match:
                indent = len(match.group(1))
                list_text = match.group(3)
                p = doc.add_paragraph(style='List Number')
                if indent > 0:
                    p.paragraph_format.left_indent = Pt(indent * 10)
                add_formatted_run(p, list_text)
        # 引用块
        elif line.startswith('> '):
            p = doc.add_paragraph()
            p.paragraph_format.left_indent = Pt(30)
            add_formatted_run(p, line[2:])
            for run in p.runs:
                run.font.color.rgb = RGBColor(100, 100, 100)
        else:
            # 普通段落
            p = doc.add_paragraph()
            add_formatted_run(p, line)

        i += 1


def add_unicode_paragraph(doc, text):
    """
    向 Word 文档添加段落，自动处理非 ASCII 字符的字体支持
    
    Args:
        doc: Document 对象
        text: 段落文本
    
    Returns:
        p: 段落对象
    """
    p = doc.add_paragraph(text)
    try:
        text.encode('ascii')
    except UnicodeEncodeError:
        from docx.oxml.ns import qn
        for run in p.runs:
            run.font.name = 'Arial'
            r = run._element
            r.rPr.rFonts.set(qn('w:eastAsia'), 'Arial')
    return p


def create_docx_with_unicode_title(doc, title_text, level=0):
    """
    向 Word 文档添加标题，自动处理非 ASCII 字符的字体支持
    
    Args:
        doc: Document 对象
        title_text: 标题文本
        level: 标题级别（0-4）
    
    Returns:
        title: 标题段落对象
    """
    title = doc.add_heading(title_text, level)
    try:
        title_text.encode('ascii')
    except UnicodeEncodeError:
        from docx.oxml.ns import qn
        for run in title.runs:
            run.font.name = 'Arial'
            r = run._element
            r.rPr.rFonts.set(qn('w:eastAsia'), 'Arial')
    return title


def save_and_send_docx(doc, filename, fallback_filename, send_file_func=None):
    """
    保存 Word 文档到内存流并返回 Flask 响应
    
    Args:
        doc: Document 对象
        filename: 完整文件名（支持 Unicode）
        fallback_filename: ASCII 后备文件名
        send_file_func: send_file 函数（默认从 flask 导入）
    
    Returns:
        Flask Response 对象
    """
    from io import BytesIO
    from flask import send_file
    
    doc_stream = BytesIO()
    doc.save(doc_stream)
    doc_stream.seek(0)
    
    response = send_file(
        doc_stream,
        as_attachment=False,
        mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
    )
    
    set_download_filename_header(response, filename, fallback_filename)
    return response


def get_recording_display_title(recording, fallback='未命名录音'):
    """
    返回用户友好的录音标题,用于文档导出
    
    Args:
        recording: 录音对象
        fallback: 默认标题
    
    Returns:
        str: 录音标题
    """
    title = (recording.title or '').strip() if recording else ''
    return title or fallback


def sanitize_download_filename_component(value, fallback=''):
    """
    清理下载文件名,保留 Unicode 字符但移除文件名中无效的字符
    
    Args:
        value: 原始文件名
        fallback: 默认值
    
    Returns:
        str: 清理后的文件名
    """
    safe_value = re.sub(r'[<>:"/\\|?*]', '', str(value or '')).strip()
    safe_value = re.sub(r'\s+', '_', safe_value)
    safe_value = safe_value.strip('._')
    return safe_value or fallback


def build_docx_download_filename(prefix, recording, recording_id, extra_parts=None):
    """
    构建本地化的 DOCX 下载文件名,支持安全的 Unicode 标题
    
    Args:
        prefix: 文件名前缀
        recording: 录音对象
        recording_id: 录音 ID
        extra_parts: 额外的文件名部分列表
    
    Returns:
        str: 完整的下载文件名
    """
    parts = [sanitize_download_filename_component(prefix, '文档')]
    for part in extra_parts or []:
        safe_part = sanitize_download_filename_component(part)
        if safe_part:
            parts.append(safe_part)

    parts.append(
        sanitize_download_filename_component(
            get_recording_display_title(recording),
            f'录音_{recording_id}'
        )
    )
    return '_'.join(parts) + '.docx'


def set_download_filename_header(response, filename, fallback_filename):
    """
    设置下载文件名响应头,支持 UTF-8 和 ASCII 后备
    
    Args:
        response: Flask 响应对象
        filename: 完整文件名(支持 Unicode)
        fallback_filename: ASCII 后备文件名
    
    Returns:
        response: 设置了文件名头的响应对象
    """
    fallback = (
        sanitize_download_filename_component(fallback_filename)
        .encode('ascii', 'ignore')
        .decode('ascii')
        or 'download.docx'
    )
    encoded_filename = quote(filename)
    response.headers['Content-Disposition'] = (
        f'attachment; filename="{fallback}"; filename*=utf-8\'\'{encoded_filename}'
    )
    return response
