"""发票数据模型与 CSV 工具

定义 CSV 列结构，提供发票分类和 CSV 读写功能。

对外接口:
    load_csv(path)            读取支付记录 CSV
    save_csv(payment_records, path)  保存支付记录 CSV
    save_invoice_csv(payment_records, path)  保存发票级别 CSV
    save_application_json(applications, path)  保存出差申请单 JSON
"""

import csv
import json
from pathlib import Path

from .. import get_logger

log = get_logger("invoice")

# CSV 列名
INVOICE_LEVEL_COLUMNS = [
    "index",
    "invoice_type",
    "invoice_number",
    "invoice_date",
    "item_name",
    "spec_model",
    "total_amount",
    "seller_name",
    "departure",
    "arrival",
    "train_no",
    "ride_date",
    "seat_class",
    "person_name",
    "card_date",
    "card_no",
    "card_amount",
    "remark",
    "person_id",
]

PAYMENT_RECORD_COLUMNS = [
    "index",
    "card_date",
    "card_no",
    "card_amount",
    "relative_invoice_count",
    "invoice_detail",
    "remark",
    "_matched_invoices",
    "person_id",
]


def _is_application_document(invoice_type: str) -> bool:
    """判断是否为出差事前申请单"""
    return invoice_type == "application"


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 _is_application_document(inv_type):
            application.append(inv)
        elif inv_type in ("train", "hotel"):
            travel.append(inv)
        else:
            general.append(inv)
    return {"travel": travel, "general": general, "application": application}


# ------------------------------------------------------------------
# CSV 读写工具
# ------------------------------------------------------------------


def _clean_invoice_for_json(inv: dict[str, str]) -> dict[str, str]:
    """清理发票字典中的内部字段，保留可序列化的字段"""
    clean = {}
    for k, v in inv.items():
        if k.startswith("_"):
            continue
        clean[k] = v
    return clean


def _load_csv(
    csv_path: Path,
    required_columns: list[str],
    label: str = "CSV",
) -> list[dict[str, str]] | None:
    """通用 CSV 读取器：按 required_columns 校验列，失败返回 None"""
    try:
        with open(csv_path, encoding="utf-8-sig", newline="") as f:
            reader = csv.DictReader(f)
            fieldnames = reader.fieldnames or []
            missing = [c for c in required_columns if c not in fieldnames]
            if missing:
                log.error(f"{label} 缺少必要列: {missing}")
                return None
            return [row for row in reader]
    except FileNotFoundError:
        log.error(f"{label} 文件不存在: {csv_path.name}")
        return None
    except Exception as e:
        log.error(f"{label} 读取失败: {e}")
        return None


def load_csv(csv_path: Path) -> list[dict[str, str]] | None:
    """读取支付记录 CSV 为 dict 列表，失败返回 None"""
    return _load_csv(csv_path, PAYMENT_RECORD_COLUMNS, "CSV")


def load_invoice_csv(csv_path: Path) -> list[dict[str, str]] | None:
    """读取发票级别 CSV 为 dict 列表（每行一张发票），失败返回 None"""
    return _load_csv(csv_path, INVOICE_LEVEL_COLUMNS, "发票 CSV")


def save_csv(
    payment_records: list[dict[str, str]],
    output_path: str | Path = "payment_records.csv",
) -> None:
    """将支付记录列表保存为 CSV（以支付记录为主键）

    每条支付记录包含：
        - card_date, card_no, card_amount（支付信息）
        - relative_invoice_count, invoice_detail（发票聚合信息）
        - _matched_invoices（内部字段，序列化为 JSON 存储在 CSV 中）
    """
    csv_path = Path(output_path)

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

        for idx, record in enumerate(payment_records, 1):
            matched_invoices: list[dict[str, str]] = record.get("_matched_invoices", [])  # type: ignore[assignment]
            invoices_json = json.dumps(
                [_clean_invoice_for_json(inv) for inv in matched_invoices],
                ensure_ascii=False,
            )

            writer.writerow(
                [
                    idx,
                    record.get("card_date", ""),
                    record.get("card_no", ""),
                    record.get("card_amount", ""),
                    record.get("relative_invoice_count", str(len(matched_invoices))),
                    record.get("invoice_detail", ""),
                    record.get("remark", ""),
                    invoices_json,
                    record.get("person_id", ""),
                ]
            )

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


def save_invoice_csv(
    payment_records: list[dict[str, str]],
    output_path: str | Path = "invoice_summary.csv",
) -> None:
    """将支付记录展平为发票级别 CSV（每行一张发票）

    从 _matched_invoices 中还原每张发票，回填刷卡信息，
    生成以发票为主键的 CSV，用于人工填写报销单参考。
    出差事前申请单不会被写入此文件（它们有独立的 CSV）。
    """
    csv_path = Path(output_path)

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

        idx = 1
        for record in payment_records:
            matched_invoices: list[dict[str, str]] = record.get("_matched_invoices", [])  # type: ignore[assignment]
            for inv in matched_invoices:
                clean_inv = _clean_invoice_for_json(inv)
                if _is_application_document(clean_inv.get("invoice_type", "")):
                    continue
                writer.writerow(
                    [
                        idx,
                        clean_inv.get("invoice_type", ""),
                        clean_inv.get("invoice_number", ""),
                        clean_inv.get("invoice_date", ""),
                        clean_inv.get("item_name", ""),
                        clean_inv.get("spec_model", ""),
                        clean_inv.get("total_amount", ""),
                        clean_inv.get("seller_name", ""),
                        clean_inv.get("departure", ""),
                        clean_inv.get("arrival", ""),
                        clean_inv.get("train_no", ""),
                        clean_inv.get("ride_date", ""),
                        clean_inv.get("seat_class", ""),
                        clean_inv.get("person_name", ""),
                        record.get("card_date", ""),
                        record.get("card_no", ""),
                        record.get("card_amount", ""),
                        record.get("remark", ""),
                        record.get("person_id", ""),
                    ]
                )
                idx += 1

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


def save_application_json(
    applications: list[dict[str, str]],
    output_path: str | Path = "travel_applications.json",
) -> None:
    """将出差事前申请单列表保存为独立 JSON 文件

    使用 JSON 保留完整嵌套结构（如出差人员信息的列表形式），
    避免 CSV 扁平化导致的字段丢失。
    """
    json_path = Path(output_path)

    with open(json_path, "w", encoding="utf-8") as f:
        json.dump(applications, f, ensure_ascii=False, indent=2)

    log.info(f"出差申请单 JSON 已保存: {json_path.name}")
