import re
import threading
import fitz
from PyPDF2 import PdfReader
from docx import Document
def extract_common_header(pdf_path):
    """
    提取 PDF 文件的公共页眉。

    参数：
    - pdf_path: PDF 文件路径。

    返回：
    - common_header: 公共页眉内容，字符串。如果未找到，则返回空字符串。
    """

    def get_headers(pdf_document, start_page, pages_to_read, is_pypdf2):
        headers = []
        for i in range(start_page, min(start_page + pages_to_read,
                                       len(pdf_document.pages) if is_pypdf2 else pdf_document.page_count)):
            try:
                if is_pypdf2:
                    page = pdf_document.pages[i]
                    text = page.extract_text() or ""
                else:
                    page = pdf_document.load_page(i)
                    text = page.get_text() or ""
                if text:
                    # 只取每页的前三行，去除前后的空白字符
                    first_lines = [line.strip() for line in text.strip().split('\n')[:3]]
                    headers.append(first_lines)
            except Exception as e:
                print(f"提取第 {i} 页文本时出错: {e}")
                continue
        return headers

    def find_common_headers(headers):
        if not headers:
            return []

        # 转置，使得每一行对应所有页的同一行
        transposed_headers = list(zip(*headers))
        common_headers = []

        for lines in transposed_headers:
            # 将每行按空格分割成部分
            split_lines = [line.split() for line in lines]

            # 找出所有行中最短的部分数
            min_parts = min(len(parts) for parts in split_lines)
            if min_parts == 0:
                continue

            common_parts = []
            for part_idx in range(min_parts):
                # 获取当前部分在所有行中的值
                current_parts = [parts[part_idx] for parts in split_lines]
                # 检查所有部分是否相同
                if all(part == current_parts[0] for part in current_parts[1:]):
                    common_parts.append(current_parts[0])
                else:
                    break  # 如果某部分不相同，停止进一步比较

            if common_parts:
                # 将共同的部分重新组合成字符串
                common_header_line = ' '.join(common_parts)
                if len(common_header_line) >= 5:  # 可以根据实际情况调整最小长度
                    common_headers.append(common_header_line)

        return common_headers

    try:
        # 尝试使用 PyPDF2 读取 PDF
        try:
            pdf_document = PdfReader(pdf_path)
            total_pages = len(pdf_document.pages)
            is_pypdf2 = True
            # print("使用 PyPDF2 成功读取 PDF 文件。")
        except Exception as e_pypdf2:
            print(f"extract_common_header:使用 PyPDF2 读取 PDF 失败: {e_pypdf2}")
            try:
                # 如果 PyPDF2 失败，尝试使用 PyMuPDF 读取 PDF
                pdf_document = fitz.open(pdf_path)
                total_pages = pdf_document.page_count
                is_pypdf2 = False
                # print("使用 PyMuPDF 成功读取 PDF 文件。")
            except Exception as e_fitz:
                print(f"extract_common_header:使用 PyMuPDF 读取 PDF 也失败: {e_fitz}")
                return ""  # 或者根据需求抛出异常

        # 定义两个提取策略
        strategies = []
        if total_pages >= 3:
            # 策略1：中间的3页
            middle_page = total_pages // 2
            start_page = max(0, middle_page - 1)
            strategies.append((start_page, 3))
        elif total_pages == 2:
            # 策略1：2页
            strategies.append((0, 2))
        else:
            # 策略1：1页
            strategies.append((0, 1))

        # 策略2：前三页
        if total_pages >= 3:
            strategies.append((0, 3))
        elif total_pages == 2:
            strategies.append((0, 2))
        elif total_pages == 1:
            strategies.append((0, 1))

        common_headers = []

        for idx, (start, count) in enumerate(strategies):
            headers = get_headers(pdf_document, start, count, is_pypdf2)
            if len(headers) < 2:
                continue  # 需要至少2页来比较

            current_common = find_common_headers(headers)
            if current_common:
                common_headers = current_common
                # print(f"使用策略{idx + 1}找到共同的页眉: {common_headers}")
                break  # 找到共同部分后退出
            # 如果没有找到，继续下一个策略

        return '\n'.join(common_headers)

        # 执行策略1和策略2：先执行策略1（中间3页或根据页数调整），然后执行策略2（前三页或根据页数调整）。
        # 获取两个策略的公共前缀：分别获取两个策略提取的公共页眉。
        # 选择最长的公共前缀：如果两个策略的前缀之间存在包含关系，选择最长的那个。如果没有包含关系，则返回策略1提取的前缀。
        # headers_results = []
        #
        # for idx, (start, count) in enumerate(strategies):
        #     headers = get_headers(pdf_document, start, count, is_pypdf2)
        #     if len(headers) < 2:
        #         continue  # 需要至少2页来比较
        #
        #     current_common = find_common_headers(headers)
        #     if current_common:
        #         headers_results.append(current_common)
        #         # 不再中断，继续执行下一个策略
        #     # 如果没有找到，继续下一个策略
        #
        # if not headers_results:
        #     return ""  # 没有找到任何公共页眉
        # # 现在有两个（或一个）策略的公共页眉，选择最长的公共前缀
        # # 首先，取出所有策略的公共页眉，并将其转换为单个字符串
        # headers_strategies = ['\n'.join(headers) for headers in headers_results]
        # # 找到最长的公共前缀
        # longest_common = max(headers_strategies, key=lambda s: len(s))
        #
        # # 检查其他策略的前缀是否是最长前缀的子集
        # is_contained = all(longest_common.startswith(other) for other in headers_strategies)
        #
        # if is_contained:
        #     # 如果所有策略的前缀都是最长前缀的子集，返回最长前缀
        #     return longest_common
        # else:
        #     # 如果没有包含关系，返回策略1的前缀（即第一个策略的结果）
        #     return headers_strategies[0]

    except Exception as e:
        print(f"Error in extract_common_header: {e}")
        return ""  # 根据需求调整返回值

def clean_page_content(text, common_header):
    # 首先删除抬头公共部分
    if common_header:  # 确保有公共抬头才进行替换
        for header_line in common_header.split('\n'):
            if header_line.strip():  # 只处理非空行
                # 替换首次出现的完整行
                text = re.sub(r'^' + re.escape(header_line.strip()) + r'\n?', '', text, count=1)
    # 预处理：删除文本开头的所有空白字符（包括空格、制表符等）
    text = text.lstrip()
    # 删除文本开头的“第 x 页”格式的页码
    text = re.sub(r'^第\s*\d+\s*页\s*', '', text)
    # 删除页码 eg:89/129   这个代码分三步走可以把89/129完全删除
    text = re.sub(r'^\s*\d+\s*(?=\D)', '', text)  # 删除开头的页码，仅当紧跟非数字字符时  投标人须知这块， 页码和顶部序号混在一起的时候也会把序号给去除了。'2018.' 20为页码 18.为序号
    text = re.sub(r'^\s*\/?\s*(共\s*)?\d+\s*(页)?\s*', '', text)   #删除/123  /共123 /共123页 /123页
    text = re.sub(r'^\s*[—-]\s*(第\s*)?\d{1,3}\s*(页)?\s*[—-]\s*', '', text)  # 删除形如 '—2—', '-2-', 或 '-第2页-' 的页码
    return text

def read_page_text_with_timeout(page, timeout=3):
    """
    尝试在子线程里执行 page.extract_text()。
    如果超过 `timeout` 秒没有完成，就返回 None，表示超时。
    如果正常完成，就返回提取到的文本。
    """
    done = threading.Event()
    result = []

    def wrapper():
        try:
            txt = page.extract_text()  # 可能会卡住的操作
            result.append(txt)
        except Exception as e:
            print("提取文本时出错：", e)
            result.append("")
        finally:
            done.set()

    # 启动后台线程
    thread = threading.Thread(target=wrapper, daemon=True)
    thread.start()

    # 等待提取结果，超过 timeout 秒则视为超时
    finished_in_time = done.wait(timeout)
    if not finished_in_time:
        print(f"单页文本提取超时（超过 {timeout} 秒）！")
        return None

    return result[0] if result else ""

def is_scanned_pdf(file_path, max_pages=15, page_timeout=3, overall_timeout=30):
    """
    检查 PDF 是否为扫描件。逻辑:
    1. 逐页读取，若某页提取文本超时(>page_timeout秒)，直接判定为 True(假设超时为扫描件)。
    2. 如果正常提取到文本且文本不为空，则判定为 False（非扫描件），立即返回。
    3. 如果前 max_pages 页都检测完成，均无可见文本，则返回 True（认为是扫描件）。
    4. 如果需要整体超时（overall_timeout），则在最外层加一个封装线程进行控制。
    """
    def core_check(result_container, done_event):
        """
        真正的核心逻辑，执行完后把结果塞进 result_container[0]，
        然后调用 done_event.set() 告知“整个检查流程结束”。
        """
        try:
            with open(file_path, 'rb') as f:
                reader = PdfReader(f)
                for i, page in enumerate(reader.pages):
                    if i >= max_pages:
                        break

                    # 尝试在 page_timeout 内获取文本
                    text = read_page_text_with_timeout(page, page_timeout)
                    if text is None:
                        print(f"第 {i+1} 页文本提取超时，直接判定为扫描件。")
                        result_container[0] = True
                        done_event.set()
                        return

                    if text.strip():
                        print(f"第 {i+1} 页检测到文本，判定为非扫描件。")
                        result_container[0] = False
                        done_event.set()
                        return
        except Exception as e:
            print("处理 PDF 文件时发生异常：", e)
            result_container[0] = True
            done_event.set()
            return

        print(f"前 {max_pages} 页均未检测到文本，判定为扫描件。")
        result_container[0] = True
        done_event.set()

    result_container = [None]  # 用于在子线程中传递结果
    done = threading.Event()
    thread = threading.Thread(target=core_check, args=(result_container, done), daemon=True)
    thread.start()

    # 等待整体流程结束，最多等待 overall_timeout 秒
    finished_in_time = done.wait(overall_timeout)
    if not finished_in_time:
        print(f"整体检查超时（超过 {overall_timeout} 秒），返回默认结果。")
        return True  # 或者根据需要返回其它默认值

    # 打印最终结果调试信息
    if result_container[0]:
        print("最终结果：认为是扫描件（未检测到有效文本或发生单页超时）")
    else:
        print("最终结果：认为非扫描件（检测到有效文本）")
    return result_container[0]

def is_pure_image(docx_path, percentage=0.3):
    """
    判断 docx 文件是否为纯图片。
    先计算文档中段落数量，然后取前 percentage(默认30%)的段落进行判断。
    如果这些段落中没有文本，则视为纯图片。
    """
    document = Document(docx_path)
    paragraphs = document.paragraphs
    total_paragraphs = len(paragraphs)
    # 计算需要判断的段落数，至少判断1个段落
    check_count = max(1, int(total_paragraphs * percentage))

    # 判断这部分段落是否含有文本
    for paragraph in paragraphs[:check_count]:
        if paragraph.text.strip():
            return False
    return True

if __name__ == '__main__':
    pdf_path=r"C:\Users\Administrator\Desktop\ztbfile.pdf"
    res=is_scanned_pdf(pdf_path)
    if res:
        print("扫描型")
    else:
        print("普通型")