"""
报销全流程编排

将发票提取 → 差旅/普通信息提取 → 浏览器填报串联为一条管道，
数据在内存中流转，同时生成 CSV 中间产物。

发票类型区分：
    - 差旅发票（train/hotel）：不生成易耗品出库单，走差旅报销流程
    - 普通发票（general）：生成易耗品出库单，走普通报销流程

数据流变更（2026-06-11）：
    在发票提取和匹配完成后立即判断报销类型（差旅/普通），
    差旅调用 LLM 提取 travel_info.json，普通预留 normal_info.json。
    Bot 仅负责接收信息并填报，不再承担信息提取职责。
"""

import json
from pathlib import Path
from typing import Any, cast

from . import get_logger
from .config import load_config
from .doc.extractor import extract_invoices
from .doc.invoice import (
    save_application_json,
    save_invoice_csv,
)
from .doc.invoice import (
    save_csv as save_payment_csv,
)
from .doc.llm_extractor import (
    CACHE_DIR_NAME,
    extract_normal_info,
    extract_travel_info,
    load_cache,
)


def _classify_invoice_batch(
    invoices: list[dict[str, str]],
) -> dict[str, list[dict[str, str]]]:
    """按发票类型分组"""
    travel: list[dict[str, str]] = []
    general: list[dict[str, str]] = []
    application: list[dict[str, str]] = []
    for inv in invoices:
        inv_type = inv.get("invoice_type", "general")
        if inv_type == "application":
            application.append(inv)
        elif inv_type in ("train", "hotel"):
            travel.append(inv)
        else:
            general.append(inv)
    return {"travel": travel, "general": general, "application": application}


log = get_logger("pipeline")


def _classify_from_cache(cache_path: Path) -> dict[str, list[dict[str, Any]]]:
    """从缓存目录读取发票数据并按类型分组"""
    from .doc.llm_extractor import load_cache

    cache_map = load_cache(cache_path)
    invoices = [data for data in cache_map.values() if data.get("invoice_type") not in ("application", "payment")]
    return _classify_invoice_batch(invoices)


def _extract_travel_info_if_needed(groups: dict[str, list[dict[str, Any]]], cache_path: Path) -> dict[str, Any] | None:
    """当存在差旅发票时，调用 LLM 提取差旅信息并缓存。

    Returns:
        差旅信息字典，非差旅时返回 None。
    """
    if not groups.get("travel"):
        return None

    from .doc.llm_extractor import CACHE_DIR_NAME, load_cache

    # 检查缓存是否已有
    cache_map = load_cache(cache_path)
    if cache_map.get("travel_info"):
        log.info("使用已有差旅信息缓存")
        return cast(dict[str, Any] | None, cache_map["travel_info"])

    log.info("开始提取差旅信息...")
    travel_info = extract_travel_info(source_dir=cache_path)

    # 保存到缓存
    cache_dir = cache_path / CACHE_DIR_NAME
    cache_dir.mkdir(parents=True, exist_ok=True)

    with open(cache_dir / "travel_info.json", "w", encoding="utf-8") as f:
        json.dump(travel_info, f, ensure_ascii=False, indent=2)
    log.info("差旅信息已保存到缓存")
    return travel_info


def _extract_normal_info_if_needed(groups: dict[str, list[dict[str, Any]]], cache_path: Path) -> dict[str, Any] | None:
    """当存在普通发票时，调用 LLM 提取普通报销信息并缓存。

    Returns:
        普通报销信息字典，非普通时返回 None。
    """
    if not groups.get("general"):
        return None

    # 检查缓存是否已有
    cache_map = load_cache(cache_path)
    if cache_map.get("normal_info"):
        log.info("使用已有普通发票信息缓存")
        return cast(dict[str, Any] | None, cache_map["normal_info"])

    log.info("开始提取普通发票信息...")
    normal_info = extract_normal_info(source_dir=cache_path)

    # 保存到缓存
    cache_dir = cache_path / CACHE_DIR_NAME
    cache_dir.mkdir(parents=True, exist_ok=True)

    with open(cache_dir / "normal_info.json", "w", encoding="utf-8") as f:
        json.dump(normal_info, f, ensure_ascii=False, indent=2)
    log.info("普通发票信息已保存到缓存")
    return normal_info


def run_pipeline(
    step: str = "all",
    username: str | None = None,
    password: str | None = None,
    cache_dir: str | None = None,
) -> int:
    """执行报销流程

    Args:
        step: all | invoice | submit
        username: 覆盖 config.json 中的用户名
        password: 覆盖 config.json 中的密码
        cache_dir: 发票缓存目录（包含 .invoice_cache 子目录）
    """
    config = load_config()
    if username:
        config["username"] = username
    if password:
        config["password"] = password

    project_dir = Path(__file__).parent.parent
    cache_path = Path(cache_dir) if cache_dir else project_dir / "scripts" / "data"

    # --------------------------------------------------
    # Step 1: 发票提取 + 类型判断 + 信息提取
    # --------------------------------------------------
    payment_records: list[dict[str, str]] | None = None
    groups: dict[str, list[dict[str, str]]] | None = None
    travel_info: dict[str, Any] | None = None
    normal_info: dict[str, Any] | None = None

    if step in ("all", "invoice"):
        log.info("=" * 60)
        log.info("[1/2] 发票提取")
        log.info("=" * 60)

        payment_records, applications, groups = extract_invoices(str(cache_path))
        if not payment_records:
            log.error("未提取到任何发票数据")
            return 1

        save_payment_csv(payment_records, cache_path / "payment_records.csv")
        save_invoice_csv(payment_records, cache_path / "invoice_summary.csv")

        if applications:
            save_application_json(applications, cache_path / "travel_applications.json")

        log.info(f"发票分类: 差旅 {len(groups['travel'])} 张, 普通 {len(groups['general'])} 张")

        # 发票提取完成后立即判断类型
        is_travel = bool(groups["travel"]) and not bool(groups["general"])
        if is_travel:
            travel_info = _extract_travel_info_if_needed(groups, cache_path)
        else:
            normal_info = _extract_normal_info_if_needed(groups, cache_path)

        if step == "invoice":
            log.info("[1/2] 发票提取 完成")
            return 0

    # --------------------------------------------------
    # Step 2: 浏览器填报
    # --------------------------------------------------
    if step in ("all", "submit"):
        log.info("=" * 60)
        log.info("[2/2] 报销提交")
        log.info("=" * 60)

        from .bot import run_bot

        if groups is None:
            groups = _classify_from_cache(cache_path)

        is_travel = bool(groups["travel"]) and not bool(groups["general"])
        if is_travel:
            if travel_info is None:
                travel_info = _extract_travel_info_if_needed(groups, cache_path)
            log.info("检测到纯差旅发票，使用差旅报销模式")
            run_bot(config, work_dir=cache_path, travel_info=travel_info)
        else:
            if normal_info is None:
                normal_info = _extract_normal_info_if_needed(groups, cache_path)
            log.info("检测到普通发票，使用普通报销模式")
            run_bot(config, work_dir=cache_path, normal_info=normal_info)

        if step == "submit":
            log.info("[2/2] 报销提交 完成")
            return 0

    log.info("=" * 60)
    log.info("全流程执行完毕")
    log.info("=" * 60)
    return 0
