# -*- coding: utf-8 -*-
"""
五组副本验证（测试专用，不属于业务文稿；验证脚本与日志保留、不删除）

在《利息计算-复核终稿.py》的独立副本上验证五种情况的停算/执行状态与数字一致性
（所有参数填充均系"测试假设"，仅验证脚本门槛逻辑，不构成任何真实核定或法律授权）：
  组A 缺依据——值与核定状态齐备、但"本金基数"依据空白 → 停算，命中1项；
  组B 仅填值——只填值、核定状态与依据留默认 → 仍停算，命中7项（仅填值不视为已核）；
  组C 三项齐备——值＋"已核定"＋依据均填 → 正式计算【执行】，数字与独立复算一致；
  组D 截止早于借款日——测算截止日=2024-03-09（早于后两笔起息日）→ 日期违例停算2项，
      演示算例不出任何数字；
  组E 截止早于还款日——测算截止日=2024-06-30（三笔借款后、2024-09-02付款日之前）
      → 不扣减、不倒算该款；其正式计算状态与默认实跑完全一致（同为中止3项）。
  组0 另对交接完成版根目录默认实跑结果做数字独立复算（复现运行1）。

每组同时核验：第四节／第七节／报告尾三处状态口径一致；无✗；互引、一致性、撤回检查通过。
用法：python3 复核验证_五组副本测试.py　（在本目录运行）
依赖：仅Python标准库。
"""
import hashlib
import os
import re
import shutil
import subprocess
import sys
from datetime import date
from decimal import Decimal, ROUND_HALF_UP

TEST = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(TEST)  # 交接完成版/
SCRIPT = "利息计算-复核终稿.py"
COPY_FILES = [SCRIPT, "起诉状-复核终稿.txt", "证据目录-复核终稿.txt",
              "待核清单-复核终稿.txt", "通话转写-原第2轮.txt"]
REPORT = "利息计算结果-复核终稿_运行1.txt"
LOG_PATH = os.path.join(TEST, "复核验证日志.txt")

D2 = Decimal("0.01")
借款 = [(Decimal("80000"), date(2024, 3, 8)),
       (Decimal("10000"), date(2024, 3, 10)),
       (Decimal("20000"), date(2024, 3, 12))]


def 舍(x):
    return x.quantize(D2, rounding=ROUND_HALF_UP)


def 独立复算(止, 名义, 式样):
    """与脚本同口径逐笔复算：算头不算尾；月÷30、年÷365；逐笔舍入后加总。"""
    日利率 = 名义 / Decimal(30) if 式样 == "月" else 名义 / Decimal(365)
    总 = Decimal("0")
    明细 = []
    for p, d0 in 借款:
        n = (止 - d0).days
        i = 舍(p * 日利率 * n)
        明细.append((n, i))
        总 += i
    return 明细, 总


def d(s):
    return Decimal(s.replace(",", ""))


日志行 = []
总检查 = 通过数 = 0


def 检查(组, 条件, ok, 备注=""):
    global 总检查, 通过数
    总检查 += 1
    通过数 += 1 if ok else 0
    s = f"[{'通过' if ok else '未过'}] {组}｜{条件}" + (f"｜{备注}" if 备注 else "")
    日志行.append(s)
    print(s)


def 节取(t, 起, 止):
    i = t.index(起) + len(起)
    j = t.index(止, i)
    return t[i:j]


def 三处状态(组, t):
    四 = 节取(t, "四、正式计算", "五、演示算例")
    七 = 节取(t, "七、口径与结果保存说明", "—— 报告完")
    尾 = t[t.index("—— 报告完"):]
    s4 = "【执行】" if "【执行】" in 四 else ("【中止】" if "【中止】" in 四 else "?")
    s7 = "已执行" if "正式计算本轮已执行" in 七 else ("中止" if "正式计算本轮中止" in 七 else "?")
    sf = "已执行" if "正式计算：已执行" in 尾 else ("中止" if "正式计算：中止" in 尾 else "?")
    检查(组, "第四节状态", s4 in ("【执行】", "【中止】"), s4)
    检查(组, "第七节状态", s7 in ("已执行", "中止"), s7)
    检查(组, "报告尾状态", sf in ("已执行", "中止"), sf)
    s4n = {"【执行】": "已执行", "【中止】": "中止"}.get(s4, s4)  # 第四节标记与第七节措辞归一
    检查(组, "三处状态口径一致", s4n == s7 == sf, f"{s4}/{s7}/{sf}")
    检查(组, "无✗行", "✗" not in t)
    检查(组, "互引检查全部通过", "互引检查：全部通过" in 尾)
    检查(组, "一致性抽查全部通过", "一致性抽查：全部通过" in 尾)
    检查(组, "撤回检查干净", "撤回检查：干净" in 尾)
    return 四, 七, 尾, s4


def 截止自报告(t):
    m = re.search(r"截止日 (\d{4}-\d{2}-\d{2})", 节取(t, "五、演示算例", "前置声明"))
    y, mo, dy = map(int, m.group(1).split("-"))
    return date(y, mo, dy), m.group(1)


def 命中数(四):
    m = re.search(r"停算条件命中 (\d+) 项", 四)
    七 = None
    return int(m.group(1)) if m else None


# ── 参数区替换对（旧串在脚本中应恰出现1次，替换时断言） ──
O_截止 = '测算截止日 = 参数(状态="演示用", 依据="未填＝取运行日（仅演示用，不是起诉日）")'


def 三联全填(基数依据):
    return [
        ("计息本金表 = 参数()",
         '计息本金表 = 参数(状态="已核定", 依据="测试假设：起诉状自动读取三笔本金及起息日，副本视为已核定（非真实核定）")'),
        ("计息方案 = 参数()",
         '计息方案 = 参数(值="方案一", 状态="已核定", 依据="测试假设：采方案一（非真实核定）")'),
        ("起诉日 = 参数()",
         '起诉日 = 参数(值=date(2026, 11, 2), 状态="已核定", 依据="测试假设：暂定起诉日（非真实核定）")'),
        ("利率口径 = 参数()",
         '利率口径 = 参数(值="月1%", 状态="已核定", 依据="测试假设：按月1%读法（非真实核定）")'),
        ("抵充口径 = 参数()",
         '抵充口径 = 参数(值="抵息", 状态="已核定", 依据="测试假设：5,000元抵息（非真实核定）")'),
        ("三千元性质 = 参数()",
         '三千元性质 = 参数(值="茶叶货款", 状态="已核定", 依据="测试假设：3,000元系茶叶货款（非真实核定）")'),
        ("本金基数 = 参数()",
         '本金基数 = 参数(值=Decimal("110000"), 状态="已核定", 依据="' + 基数依据 + '")'),
    ]


仅填值 = [
    ("计息方案 = 参数()", '计息方案 = 参数(值="方案一")'),
    ("起诉日 = 参数()", '起诉日 = 参数(值=date(2026, 11, 2))'),
    ("利率口径 = 参数()", '利率口径 = 参数(值="月1%")'),
    ("抵充口径 = 参数()", '抵充口径 = 参数(值="抵息")'),
    ("三千元性质 = 参数()", '三千元性质 = 参数(值="茶叶货款")'),
    ("本金基数 = 参数()", '本金基数 = 参数(值=Decimal("110000"))'),
]

用例 = {
    "A_缺依据": 三联全填(""),
    "B_仅填值": 仅填值,
    "C_三项齐备": 三联全填("测试假设：抵息口径110,000元（非真实核定）"),
    "D_截止早于借款日": 三联全填("测试假设：抵息口径110,000元（非真实核定）")
        + [(O_截止, '测算截止日 = 参数(值=date(2024, 3, 9), 状态="演示用", '
                    '依据="测试假设：截止日早于后两笔借款起息日（非真实核定）")')],
    "E_截止早于还款日": [(O_截止, '测算截止日 = 参数(值=date(2024, 6, 30), 状态="演示用", '
                        '依据="测试假设：截止日早于2024-09-02付款日（非真实核定）")')],
}

日志行.append("五组副本验证（测试假设参数，仅验证门槛逻辑，非真实核定）")
日志行.append(f"基脚本SHA256：{hashlib.sha256(open(os.path.join(ROOT, SCRIPT),'rb').read()).hexdigest()}")
日志行.append(f"运行目录：{TEST}")
日志行.append("")

组报告 = {}

for 组名, 补丁 in 用例.items():
    print(f"—— 组{组名} ——")
    日志行.append(f"—— 组{组名} ——")
    目录 = os.path.join(TEST, 组名)
    shutil.rmtree(目录, ignore_errors=True)  # 重跑前清空旧测试目录
    os.makedirs(目录)
    for f in COPY_FILES:
        目标 = os.path.join(目录, f)
        shutil.copyfile(os.path.join(ROOT, f), 目标)  # 仅拷内容，不继承只读权限位
        if f == SCRIPT:
            os.chmod(目标, 0o644)  # 副本需可写以便填入测试参数
    脚本路径 = os.path.join(目录, SCRIPT)
    原文 = open(脚本路径, encoding="utf-8").read()
    改后 = 原文
    for 旧, 新 in 补丁:
        n = 改后.count(旧)
        检查(组名, f"替换目标恰出现1次：{旧[:14]}…", n == 1, f"实际{n}次")
        改后 = 改后.replace(旧, 新)
    open(脚本路径, "w", encoding="utf-8").write(改后)
    cmd = [sys.executable, SCRIPT]
    r = subprocess.run(cmd, cwd=目录, capture_output=True, encoding="utf-8", errors="replace")
    open(os.path.join(目录, "运行日志_标准输出.txt"), "w", encoding="utf-8").write(r.stdout + r.stderr)
    日志行.append(f"命令：python3 {SCRIPT}　（目录 {目录}）　exit={r.returncode}")
    检查(组名, "退出码为0", r.returncode == 0, f"exit={r.returncode}")
    t = open(os.path.join(目录, REPORT), encoding="utf-8").read()
    组报告[组名] = t
    四, 七, 尾, s4 = 三处状态(组名, t)

    if 组名 == "A_缺依据":
        检查(组名, "状态为中止", s4 == "【中止】")
        检查(组名, "命中1项", 命中数(四) == 1, f"实际{命中数(四)}")
        检查(组名, "唯一命中项为本金基数·依据空白",
             "本金基数" in 四 and "依据空白" in 四 and "缺值" not in 四
             and "核定状态为“未核”" not in 四)
        检查(组名, "不产出诉请金额", "诉请填列" not in 四)

    elif 组名 == "B_仅填值":
        检查(组名, "状态为中止", s4 == "【中止】")
        检查(组名, "命中7项（每个已填值参数各1项）", 命中数(四) == 7, f"实际{命中数(四)}")
        检查(组名, "无缺值命中（值已填）", "缺值" not in 四)
        检查(组名, "逐项注明仅填值不视为已核", 四.count("仅填值不视为已核") >= 7,
             f"出现{四.count('仅填值不视为已核')}处")
        检查(组名, "不产出诉请金额", "诉请填列" not in 四)

    elif 组名 == "C_三项齐备":
        检查(组名, "状态为执行", s4 == "【执行】")
        起诉 = date(2026, 11, 2)
        明细, 毛利 = 独立复算(起诉, Decimal("0.01"), "月")
        净 = 毛利 - Decimal("5000")
        m1 = re.search(r"利息合计：([\d,]+\.\d{2}) 元；抵5,000元后净利息：([\d,]+\.\d{2}) 元", 四)
        m2 = re.search(r"诉请填列：本金 ([\d,]+) 元＋利息暂计 ([\d,]+\.\d{2}) 元"
                       r"＝([\d,]+\.\d{2}) 元（截至 (\d{4}-\d{2}-\d{2})", 四)
        检查(组名, "利息合计与独立复算一致", m1 and d(m1.group(1)) == 毛利,
             f"报告{m1.group(1) if m1 else '—'}／复算{毛利:,.2f}")
        检查(组名, "净利息＝毛利−5,000且与独立复算一致", m1 and d(m1.group(2)) == 净,
             f"报告{m1.group(2) if m1 else '—'}／复算{净:,.2f}")
        检查(组名, "诉请填列本金110,000元（抵息口径假设）", m2 and d(m2.group(1)) == Decimal("110000"))
        检查(组名, "诉请暂计与合计与独立复算一致",
             m2 and d(m2.group(2)) == 净 and d(m2.group(3)) == Decimal("110000") + 净,
             f"报告{m2.group(2)}＋{m2.group(3)}" if m2 else "—")
        检查(组名, "暂计截至日＝填定起诉日", m2 and m2.group(4) == "2026-11-02", m2.group(4) if m2 else "—")
        天 = [int(x) for x in re.findall(r"共 (\d+) 天", 四)]
        检查(组名, "三笔天数与独立复算一致", 天 == [n for n, _ in 明细], f"{天}")

    elif 组名 == "D_截止早于借款日":
        检查(组名, "状态为中止", s4 == "【中止】")
        检查(组名, "命中2项且均为日期违例", 命中数(四) == 2
             and "测算截止日 2024-03-09 早于起息日 2024-03-10" in 四
             and "测算截止日 2024-03-09 早于起息日 2024-03-12" in 四
             and 四.count("日期顺序错误（停算）") == 2, f"实际{命中数(四)}")
        检查(组名, "演示算例不出任何数字", "演示算例不出任何数字" in t)
        检查(组名, "全文无算式行", "算式：" not in t)
        检查(组名, "全文无利息合计数", "利息合计" not in t)
        检查(组名, "不产出诉请金额", "诉请填列" not in 四)

    elif 组名 == "E_截止早于还款日":
        t0 = open(os.path.join(ROOT, REPORT), encoding="utf-8").read()
        四0 = 节取(t0, "四、正式计算", "五、演示算例")
        检查(组名, "状态为中止", s4 == "【中止】")
        检查(组名, "正式计算状态与默认实跑逐字一致（同为中止3项）", 四 == 四0)
        检查(组名, "窗口规则行：早于付款日、不扣减不倒算",
             "窗口规则：测算截止日早于5,000元付款日（2024-09-02）——该款于截止日尚未支付："
             "涉及组合不扣减该款、不倒算付款日前后数据。" in t)
        截止E = date(2024, 6, 30)
        _, 毛利A = 独立复算(截止E, Decimal("0.01"), "月")
        _, 毛利C = 独立复算(截止E, Decimal("0.10"), "年")
        m1 = re.search(r"利息合计：([\d,]+\.\d{2}) 元（不扣减）；本金＋利息＝([\d,]+\.\d{2}) 元", t)
        m4 = re.search(r"利息合计：([\d,]+\.\d{2}) 元（5,000元付款日在截止日之后，不扣减、不倒算）", t)
        检查(组名, "组合一利息与独立复算一致且不扣减", m1 and d(m1.group(1)) == 毛利A,
             f"报告{m1.group(1) if m1 else '—'}／复算{毛利A:,.2f}")
        检查(组名, "组合一演示合计＝110,000＋利息", m1 and d(m1.group(2)) == Decimal("110000") + 毛利A)
        检查(组名, "组合四利息与独立复算一致且不扣减", m4 and d(m4.group(1)) == 毛利C,
             f"报告{m4.group(1) if m4 else '—'}／复算{毛利C:,.2f}")
        检查(组名, "第五节无抵息后净利息（未扣5,000）", "抵息后净利息" not in 节取(t, "五、演示算例", "六、三份文稿一致性抽查"))
        检查(组名, "无抵充时点算术注（不倒算）", "算术注" not in t)
        检查(组名, "第七节注明窗口规则已触发", "已触发" in 七)
        检查(组名, "附注不出年末参考数据（不倒算）", "不出该参考数据（不倒算）" in t)
        检查(组名, "组合六因截止早于LPR起算日不出数", "早于LPR备选起算日 2025-07-01，该组合不出数" in t)

    日志行.append("")

# ── 组0：默认实跑结果数字独立复算 ──
print("—— 组0 默认实跑结果复算 ——")
日志行.append("—— 组0 默认实跑结果复算 ——")
t0 = open(os.path.join(ROOT, REPORT), encoding="utf-8").read()
四0, 七0, 尾0, s40 = 三处状态("组0", t0)
检查("组0", "状态为中止", s40 == "【中止】")
检查("组0", "命中3项（本金三笔／计息方案／起诉日）", 命中数(四0) == 3, f"实际{命中数(四0)}")
截止0, 截止串 = 截止自报告(t0)
明细1, 毛利1 = 独立复算(截止0, Decimal("0.01"), "月")
明细4, 毛利4 = 独立复算(截止0, Decimal("0.10"), "年")
五0 = 节取(t0, "五、演示算例", "六、三份文稿一致性抽查")
组一块 = 节取(五0, "组合一", "组合二")
组四块 = 节取(五0, "组合四", "组合五")
m1 = re.search(r"利息合计：([\d,]+\.\d{2}) 元；抵息后净利息：([\d,]+\.\d{2}) 元；"
               r"本金＋净利息＝([\d,]+\.\d{2}) 元", 组一块)
m4 = re.search(r"利息合计：([\d,]+\.\d{2}) 元；抵息后净利息：([\d,]+\.\d{2}) 元", 组四块)
检查("组0", "组合一利息合计与独立复算一致", m1 and d(m1.group(1)) == 毛利1,
     f"报告{m1.group(1) if m1 else '—'}／复算{毛利1:,.2f}")
检查("组0", "组合一净利息与合计一致", m1 and d(m1.group(2)) == 毛利1 - Decimal("5000")
     and d(m1.group(3)) == Decimal("110000") + 毛利1 - Decimal("5000"))
天1 = [int(x) for x in re.findall(r"共 (\d+) 天", 组一块)]
检查("组0", "组合一三笔天数与独立复算一致", 天1 == [n for n, _ in 明细1], f"{天1}")
检查("组0", "组合四利息合计与独立复算一致", m4 and d(m4.group(1)) == 毛利4,
     f"报告{m4.group(1) if m4 else '—'}／复算{毛利4:,.2f}")
dD = (截止0 - date(2025, 7, 1)).days
iD = 舍(Decimal("105000") * Decimal("0.030") / Decimal(365) * dD)
m6 = re.search(r"算式：105,000 × 3\.0% ÷ 365 × (\d+) = ([\d,]+\.\d{2}) 元", 五0)
检查("组0", "组合六天数与金额与独立复算一致", m6 and int(m6.group(1)) == dD and d(m6.group(2)) == iD,
     f"报告{m6.group(2) if m6 else '—'}／复算{iD:,.2f}／{dD}天")
截至年末 = sum(舍(p * (Decimal("0.01") / Decimal(30)) * (date(2024, 12, 31) - d0).days)
             for p, d0 in 借款)
mf = re.search(r"已生息 ([\d,]+\.\d{2}) 元（月利率1%口径）", 五0)
检查("组0", "附注年末参考数据与独立复算一致", mf and d(mf.group(1)) == 截至年末,
     f"报告{mf.group(1) if mf else '—'}／复算{截至年末:,.2f}")
算式1 = re.findall(r"= ([\d,]+\.\d{2}) 元", 组一块)[:3]
检查("组0", "组合一逐笔算式与独立复算一致", [d(x) for x in 算式1] == [i for _, i in 明细1],
     f"{算式1}")
日志行.append("")

print()
结论 = f"总结：{通过数}/{总检查} 项检查通过。" + ("全部通过。" if 通过数 == 总检查 else "存在未过项，见上。")
print(结论)
日志行.append(结论)
日志行.append("说明：组A—E 参数均为测试假设，仅验证脚本门槛与口径；不构成真实核定或法律授权。")
open(LOG_PATH, "w", encoding="utf-8").write("\n".join(日志行) + "\n")
print(f"日志已保留：{LOG_PATH}")
sys.exit(0 if 通过数 == 总检查 else 1)
