# -*- coding: utf-8 -*-
"""
一次性脚本：验证消费用例对账公式在全表中的成立率。

连接 test_etl_feiqiu 测试库，对每个对账公式计算：
- 成立的订单数 / 不成立的订单数 / 不成立率
- 不成立时的最大偏差
- 前 5 个不成立的订单 ID 和偏差值

输出报告到 {EXPORT_ROOT}/REPORTS/formula_verification_report.md
"""
from __future__ import annotations

import os
from datetime import datetime
from decimal import Decimal
from pathlib import Path

# ── 环境加载 ──────────────────────────────────────────────
from _env_paths import get_output_path

TEST_DB_DSN = os.environ.get("TEST_DB_DSN")
if not TEST_DB_DSN:
    raise RuntimeError("环境变量 TEST_DB_DSN 未定义。请在根 .env 中配置测试库连接串。")

import psycopg2
import psycopg2.extras

# ── 输出路径 ──────────────────────────────────────────────
EXPORT_ROOT = get_output_path("EXPORT_ROOT")
OUTPUT_DIR = EXPORT_ROOT / "REPORTS"
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
OUTPUT_FILE = OUTPUT_DIR / "formula_verification_report.md"


def get_conn():
    return psycopg2.connect(TEST_DB_DSN)


def run_formula(cur, formula_id: str, formula_name: str, sql: str) -> dict:
    """
    执行一个公式验证 SQL，返回结构化结果。

    SQL 必须返回以下列：
    - order_id: 订单 ID
    - diff: 偏差值（0 表示成立）
    """
    print(f"  验证 {formula_id}: {formula_name} ...")
    cur.execute(sql)
    rows = cur.fetchall()

    total = len(rows)
    ok = sum(1 for r in rows if abs(r["diff"]) < Decimal("0.005"))
    fail = total - ok

    # 不成立的记录
    failures = sorted(
        [r for r in rows if abs(r["diff"]) >= Decimal("0.005")],
        key=lambda r: abs(r["diff"]),
        reverse=True,
    )

    max_diff = abs(failures[0]["diff"]) if failures else Decimal("0")
    top5 = failures[:5]

    result = {
        "id": formula_id,
        "name": formula_name,
        "total": total,
        "ok": ok,
        "fail": fail,
        "fail_rate": f"{fail / total * 100:.2f}%" if total > 0 else "N/A",
        "max_diff": float(max_diff),
        "top5": [
            {"order_id": r["order_id"], "diff": float(r["diff"])}
            for r in top5
        ],
    }
    print(f"    总数={total}, 成立={ok}, 不成立={fail} ({result['fail_rate']}), 最大偏差={max_diff}")
    return result


# ── 公式定义 ──────────────────────────────────────────────

FORMULAS = [
    # ── 消费结算公式 (settle_type=1) ──────────────────────

    # F1: 消费构成等式（含 pl_coupon）
    {
        "id": "F1",
        "name": "消费构成: consume = table + goods + asst_pd + asst_cx + elec + pl_coupon (settle_type=1)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money
                   - (table_charge_money + goods_money + assistant_pd_money
                      + assistant_cx_money + electricity_money + pl_coupon_sale_amount)
                   AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
        """,
    },
    # F1b: 仅最近5个月（样本采样范围）
    {
        "id": "F1b",
        "name": "消费构成(最近5月): consume = table + goods + asst + elec + pl_coupon",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money
                   - (table_charge_money + goods_money + assistant_pd_money
                      + assistant_cx_money + electricity_money + pl_coupon_sale_amount)
                   AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
              AND create_time >= NOW() - INTERVAL '5 months'
        """,
    },
    # F2: 收支平衡等式（减去 rounding）
    {
        "id": "F2",
        "name": "收支平衡: consume = point + balance + coupon + member_disc + adjust + pl_coupon - rounding",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money
                   - (point_amount + balance_amount + coupon_amount
                      + member_discount_amount + adjust_amount
                      + pl_coupon_sale_amount - rounding_amount)
                   AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
        """,
    },
    # F2b: 收支平衡（最近5月）
    {
        "id": "F2b",
        "name": "收支平衡(最近5月): consume = point + balance + coupon + disc + adjust + pl_coupon - rounding",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money
                   - (point_amount + balance_amount + coupon_amount
                      + member_discount_amount + adjust_amount
                      + pl_coupon_sale_amount - rounding_amount)
                   AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
              AND create_time >= NOW() - INTERVAL '5 months'
        """,
    },
    # F3: 台费明细一致性
    {
        "id": "F3",
        "name": "台费明细: table_charge_money = SUM(table_fee_logs.ledger_amount)",
        "sql": """
            SELECT h.order_settle_id AS order_id,
                   h.table_charge_money - COALESCE(t.total_ledger, 0) AS diff
            FROM dwd.dwd_settlement_head h
            LEFT JOIN (
                SELECT order_settle_id, SUM(ledger_amount) AS total_ledger
                FROM dwd.dwd_table_fee_log
                GROUP BY order_settle_id
            ) t ON h.order_settle_id = t.order_settle_id
            WHERE h.settle_type = 1
        """,
    },
    # F4: pay_amount = point_amount
    {
        "id": "F4",
        "name": "pay_amount = point_amount (消费结算)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   pay_amount - point_amount AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
        """,
    },
    # F5: 余额构成: balance >= gift_card + recharge_card
    {
        "id": "F5",
        "name": "余额构成: balance >= gift_card + recharge_card",
        "sql": """
            SELECT order_settle_id AS order_id,
                   balance_amount - (gift_card_amount + recharge_card_amount) AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
              AND balance_amount > 0
        """,
    },
    # F6: 抹零等式 (纯积分支付)
    {
        "id": "F6",
        "name": "抹零等式: point = consume + rounding (纯积分支付，无其他优惠)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   point_amount - (consume_money + rounding_amount) AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 1
              AND point_amount > 0
              AND balance_amount = 0
              AND coupon_amount = 0
              AND member_discount_amount = 0
              AND adjust_amount = 0
              AND pl_coupon_sale_amount = 0
        """,
    },

    # ── 商品结算 / 充值撤销 ──────────────────────────────

    # F7: 商品结算: consume = goods_money
    {
        "id": "F7",
        "name": "商品结算: consume = goods_money (settle_type=3)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money - goods_money AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 3
        """,
    },
    # F7b: 商品结算含 pl_coupon
    {
        "id": "F7b",
        "name": "商品结算: consume = goods_money + pl_coupon (settle_type=3)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money - (goods_money + pl_coupon_sale_amount) AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 3
        """,
    },
    # F8: 充值撤销: consume = pay_amount
    {
        "id": "F8",
        "name": "充值撤销: consume = pay_amount (settle_type=7)",
        "sql": """
            SELECT order_settle_id AS order_id,
                   consume_money - pay_amount AS diff
            FROM dwd.dwd_settlement_head
            WHERE settle_type = 7
        """,
    },

    # ── 充值公式 ─────────────────────────────────────────

    # R1: 充值: pay_amount = point_amount
    {
        "id": "R1",
        "name": "充值: pay_amount = point_amount (pay>0)",
        "sql": """
            SELECT recharge_order_id AS order_id,
                   pay_amount - point_amount AS diff
            FROM dwd.dwd_recharge_order
            WHERE pay_amount > 0
        """,
    },
    # R2: 充值: ex.consume_money = pay_amount
    {
        "id": "R2",
        "name": "充值: ex.consume_money = pay_amount",
        "sql": """
            SELECT r.recharge_order_id AS order_id,
                   rex.consume_money - r.pay_amount AS diff
            FROM dwd.dwd_recharge_order r
            JOIN dwd.dwd_recharge_order_ex rex USING (recharge_order_id)
            WHERE r.pay_amount > 0
        """,
    },

    # ── 余额变动公式 ─────────────────────────────────────

    # B1: 余额变动一致性
    {
        "id": "B1",
        "name": "余额变动: balance_after = balance_before + change_amount",
        "sql": """
            SELECT balance_change_id AS order_id,
                   balance_after - (balance_before + change_amount) AS diff
            FROM dwd.dwd_member_balance_change
        """,
    },
    # B2: 本金变动一致性（过滤 NULL）
    {
        "id": "B2",
        "name": "本金变动: principal_after = principal_before + principal_change",
        "sql": """
            SELECT balance_change_id AS order_id,
                   principal_after - (principal_before + principal_change_amount) AS diff
            FROM dwd.dwd_member_balance_change
            WHERE principal_after IS NOT NULL
              AND principal_before IS NOT NULL
              AND principal_change_amount IS NOT NULL
        """,
    },
]


def generate_report(results: list[dict]) -> str:
    """生成 Markdown 格式的验证报告"""
    lines = [
        "# 对账公式验证报告",
        "",
        f"> 数据库：test_etl_feiqiu DWD 层",
        f"> 生成时间：{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
        f"> 验证公式数：{len(results)}",
        "",
        "## 汇总",
        "",
        "| 编号 | 公式 | 总数 | 成立 | 不成立 | 不成立率 | 最大偏差 |",
        "|------|------|------|------|--------|----------|----------|",
    ]

    for r in results:
        status = "✅" if r["fail"] == 0 else "⚠️" if r["fail_rate"].replace("%", "") and float(r["fail_rate"].replace("%", "")) < 1 else "❌"
        lines.append(
            f"| {r['id']} | {r['name'][:50]}{'...' if len(r['name']) > 50 else ''} "
            f"| {r['total']:,} | {r['ok']:,} | {r['fail']:,} "
            f"| {r['fail_rate']} | {r['max_diff']:.2f} |"
        )

    lines.append("")
    lines.append("## 详情")
    lines.append("")

    for r in results:
        lines.append(f"### {r['id']}: {r['name']}")
        lines.append("")
        lines.append(f"- 总数：{r['total']:,}")
        lines.append(f"- 成立：{r['ok']:,}")
        lines.append(f"- 不成立：{r['fail']:,} ({r['fail_rate']})")
        lines.append(f"- 最大偏差：{r['max_diff']:.2f}")
        lines.append("")

        if r["top5"]:
            lines.append("**不成立的前 5 条记录：**")
            lines.append("")
            lines.append("| 订单 ID | 偏差 |")
            lines.append("|---------|------|")
            for t in r["top5"]:
                lines.append(f"| {t['order_id']} | {t['diff']:.2f} |")
            lines.append("")
        else:
            lines.append("（全部成立）")
            lines.append("")

    return "\n".join(lines)


def main():
    print(f"连接测试库: {TEST_DB_DSN[:30]}...")
    conn = get_conn()
    results = []

    try:
        cur = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)

        for formula in FORMULAS:
            try:
                result = run_formula(cur, formula["id"], formula["name"], formula["sql"])
                results.append(result)
            except Exception as e:
                print(f"  ❌ {formula['id']} 执行失败: {e}")
                results.append({
                    "id": formula["id"],
                    "name": formula["name"],
                    "total": 0,
                    "ok": 0,
                    "fail": 0,
                    "fail_rate": "ERROR",
                    "max_diff": 0,
                    "top5": [],
                })

        cur.close()
    finally:
        conn.close()

    # 生成报告
    report = generate_report(results)
    with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
        f.write(report)

    print(f"\n✅ 报告已生成: {OUTPUT_FILE}")
    print(f"   文件大小: {OUTPUT_FILE.stat().st_size / 1024:.1f} KB")

    # 打印汇总
    print("\n=== 汇总 ===")
    all_ok = True
    for r in results:
        status = "✅" if r["fail"] == 0 else "❌"
        print(f"  {status} {r['id']}: {r['name'][:60]} — {r['fail_rate']}")
        if r["fail"] > 0:
            all_ok = False

    if all_ok:
        print("\n🎉 所有公式全部成立！")
    else:
        print("\n⚠️ 部分公式存在不成立的记录，请查看详细报告。")


if __name__ == "__main__":
    main()
