"""
PDF 发票信息提取

从 PDF 发票文件中提取关键字段，输出为标准化的发票数据列表。

对外接口:
    extract_invoices(directory) -> list[dict]   扫描目录下所有 PDF 并提取
    save_csv(invoices, path)                    保存为 CSV
    save_markdown(invoices, path)               保存为 Markdown 汇总
"""

import csv
import re
from pathlib import Path

from . import get_logger

log = get_logger("extractor")

CSV_COLUMNS = [
    "序号", "发票号码", "开票日期", "项目名称", "规格型号",
    "价税合计", "销售方名称", "人员姓名", "刷卡日期",
    "公务卡号", "刷卡金额", "备注", "工号",
]


# ------------------------------------------------------------------
# PDF 文件发现与文本提取
# ------------------------------------------------------------------

def find_pdf_files(directory: str = ".") -> list[Path]:
    """查找目录下所有 PDF 文件（非递归）"""
    pdf_dir = Path(directory)
    if not pdf_dir.exists():
        return []
    return sorted(pdf_dir.glob("*.pdf"))


def extract_text_from_pdf(filepath: Path) -> str:
    """从单个 PDF 中提取全部文本"""
    try:
        import pdfplumber
    except ImportError:
        raise ImportError("缺少 pdfplumber，请执行: pip install pdfplumber")

    try:
        parts = []
        with pdfplumber.open(filepath) as pdf:
            for page in pdf.pages:
                text = page.extract_text()
                if text:
                    parts.append(text)
        return "\n".join(parts)
    except Exception as e:
        log.error(f"无法读取 {filepath.name}: {e}")
        return ""


# ------------------------------------------------------------------
# 字段解析
# ------------------------------------------------------------------

def _first(regexes: list[str], text: str) -> str | None:
    """尝试多个正则，返回第一个匹配组的文本"""
    for pattern in regexes:
        m = re.search(pattern, text)
        if m:
            return m.group(1).strip()
    return None


def _parse_line_item(line: str) -> dict | None:
    """解析单行明细（*分类*具体名称 格式）"""
    m = re.match(r"\*([^*]+)\*\s*(.+)", line)
    if m:
        return {
            "项目名称": f"*{m.group(1).strip()}*{m.group(2).strip()}",
            "规格型号": m.group(2).strip(),
        }
    return None


def _extract_line_items(text: str) -> list[dict]:
    """从发票文本中提取所有明细行"""
    items = []
    skip_keywords = ["项目名称", "合 计", "价税合计", "备注", "开票人"]

    for line in text.split("\n"):
        line = line.strip()
        if not line:
            continue
        if any(kw in line for kw in skip_keywords):
            continue
        if "*" in line:
            item = _parse_line_item(line)
            if item:
                items.append(item)

    return items


def _format_date(date_raw: str) -> str:
    """将「2026年5月18日」转为「2026/5/18」"""
    m = re.match(r"(\d{4})年(\d{1,2})月(\d{1,2})日", date_raw)
    if m:
        return f"{m.group(1)}/{m.group(2)}/{m.group(3)}"
    return date_raw


def parse_invoice(text: str) -> dict:
    """从发票文本中提取关键字段，返回 dict

    返回字段:
        发票号码, 开票日期, 销售方名称, 价税合计, _items (明细列表)
        其他字段（人员姓名等）留空，后续由 OCR 步骤填充
    """
    invoice: dict[str, str] = {}

    invoice["发票号码"] = _first([r"发票号码[：:]?\s*(\d+)"], text) or ""

    date_raw = _first([r"开票日期[：:]?\s*(\d{4}年\d{1,2}月\d{1,2}日)"], text) or ""
    invoice["开票日期"] = _format_date(date_raw) if date_raw else ""

    invoice["销售方名称"] = _first(
        [
            r"销\s*售?\s*方?\s*名称[：:]?\s*(.+?)(?:\n|$)",
            r"销\s*名称[：:]?\s*(.+?)(?:\n|$)",
        ],
        text,
    ) or ""

    invoice["价税合计"] = _first(
        [r"价税合计.*?（小写）[¥￥]?\s*(\d+\.?\d*)"], text
    ) or ""

    invoice["_items"] = _extract_line_items(text)

    # 以下字段无法从 PDF 提取，留空由 OCR 步骤填充
    for key in ("项目名称", "规格型号", "人员姓名", "刷卡日期",
                "公务卡号", "刷卡金额", "备注", "工号"):
        if key not in invoice:
            invoice[key] = ""

    return invoice


# ------------------------------------------------------------------
# CSV / Markdown 输出
# ------------------------------------------------------------------

def save_csv(invoices: list[dict], output_path: str | Path = "invoice_summary.csv"):
    """将发票列表保存为 CSV"""
    csv_path = Path(output_path)

    with open(csv_path, "w", encoding="utf-8", newline="") as f:
        writer = csv.writer(f)
        writer.writerow(CSV_COLUMNS)

        for idx, inv in enumerate(invoices, 1):
            items = inv.get("_items", [])
            first_item = items[0] if items else {}
            writer.writerow([
                idx,
                inv.get("发票号码", ""),
                inv.get("开票日期", ""),
                first_item.get("项目名称", inv.get("项目名称", "")),
                first_item.get("规格型号", inv.get("规格型号", "")),
                inv.get("价税合计", ""),
                inv.get("销售方名称", ""),
                inv.get("人员姓名", ""),
                inv.get("刷卡日期", ""),
                inv.get("公务卡号", ""),
                inv.get("刷卡金额", ""),
                inv.get("备注", ""),
                inv.get("工号", ""),
            ])

    log.info(f"CSV 已保存: {csv_path.name}")


def save_markdown(invoices: list[dict], output_path: str | Path = "invoice_summary.md"):
    """将发票列表保存为 Markdown 汇总表"""
    md_path = Path(output_path)
    lines = [
        "# 发票信息汇总表",
        "",
        "| 序号 | 发票号码 | 开票日期 | 项目名称 | 规格型号 | 价税合计 | 销售方名称 |",
        "|------|---------|---------|---------|---------|---------|-----------|",
    ]

    total = 0.0
    for idx, inv in enumerate(invoices, 1):
        amount = 0.0
        try:
            amount = float(inv.get("价税合计", "0"))
        except (ValueError, TypeError):
            pass
        total += amount

        items = inv.get("_items", [])
        first_item = items[0] if items else {}
        project = first_item.get("项目名称", inv.get("项目名称", "-"))
        spec = first_item.get("规格型号", inv.get("规格型号", "-"))

        lines.append(
            f"| {idx} "
            f"| {inv.get('发票号码', '')} "
            f"| {inv.get('开票日期', '')} "
            f"| {project} | {spec} "
            f"| ¥{amount:,.2f} "
            f"| {inv.get('销售方名称', '')} |"
        )

    lines.append("")
    lines.append(f"**总计: ¥{total:,.2f}**")
    lines.append("")

    with open(md_path, "w", encoding="utf-8") as f:
        f.write("\n".join(lines))

    log.info(f"Markdown 已保存: {md_path.name}")


# ------------------------------------------------------------------
# 主入口
# ------------------------------------------------------------------

def extract_invoices(directory: str = ".") -> list[dict]:
    """扫描目录下所有 PDF，提取发票信息并返回列表"""
    target_dir = Path(directory).absolute()

    pdf_files = find_pdf_files(directory)
    if not pdf_files:
        log.warning("未找到 PDF 文件")
        return []

    log.info(f"发现 {len(pdf_files)} 个 PDF 文件")

    all_invoices = []
    for pdf_path in pdf_files:
        text = extract_text_from_pdf(pdf_path)
        if text:
            invoice = parse_invoice(text)
            if invoice:
                all_invoices.append(invoice)
            else:
                log.warning(f"未能解析: {pdf_path.name}")
        else:
            log.warning(f"未能提取文本: {pdf_path.name}")

    if all_invoices:
        log.info(f"共处理 {len(all_invoices)} 张发票")
    else:
        log.warning("未成功解析任何发票")

    return all_invoices