"""虚构支付服务：独立 SQLite 账本，金额统一使用 CNY 分；不访问真实支付。"""
import hashlib
import json
import sqlite3
from contextlib import closing

# closing 负责关闭连接，后面的连接上下文负责提交/回滚事务。
from pathlib import Path


def fingerprint(tenant: str, order: str, amount: int, currency: str) -> str:
    # 使用固定字段顺序和规范 JSON，避免字典展示差异改变同一请求的指纹。
    raw = json.dumps([tenant, order, amount, currency], separators=(",", ":"))
    return hashlib.sha256(raw.encode()).hexdigest()


class PaymentSimulator:
    def __init__(self, path: Path):
        self.path = path
        path.parent.mkdir(parents=True, exist_ok=True)
        with closing(sqlite3.connect(path)) as db, db:
            db.executescript("""
                CREATE TABLE IF NOT EXISTS balances (
                    tenant TEXT, order_id TEXT, remaining INTEGER NOT NULL,
                    PRIMARY KEY (tenant, order_id));
                CREATE TABLE IF NOT EXISTS receipts (
                    operation_id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL,
                    receipt TEXT NOT NULL);
            """)
            # INSERT OR IGNORE 不会在进程重启时把已经减少的余额重新补满。
            db.execute("INSERT OR IGNORE INTO balances VALUES (?, ?, ?)",
                       ("shop-demo", "A1042", 29900))

    def lookup(self, operation_id: str, expected: str):
        # 这个模拟器查询已提交记录是完整的。真实网关若最终一致，查不到不能证明未发生。
        with closing(sqlite3.connect(self.path)) as db, db:
            row = db.execute("SELECT fingerprint, receipt FROM receipts WHERE operation_id=?",
                             (operation_id,)).fetchone()
        if row is None:
            return None
        if row[0] != expected:
            raise ValueError("operation ID 被用于不同参数")
        return json.loads(row[1])

    def refund(self, operation_id: str, tenant: str, order: str, amount: int,
               currency: str, *, lose_response: bool = False):
        if type(amount) is not int or amount <= 0 or currency != "CNY":
            # bool 是 int 的子类，必须用精确类型判断拒绝 True 被当作一分钱。
            raise ValueError("金额必须为正整数分，币种必须为 CNY")
        fp = fingerprint(tenant, order, amount, currency)
        with closing(sqlite3.connect(self.path)) as db, db:
            # 写锁覆盖余额检查、余额扣减和幂等回执，防止并发超额退款。
            db.execute("BEGIN IMMEDIATE")
            old = db.execute("SELECT fingerprint, receipt FROM receipts WHERE operation_id=?",
                             (operation_id,)).fetchone()
            if old:
                if old[0] != fp:
                    raise ValueError("同一操作的参数发生变化")
                return json.loads(old[1])
            balance = db.execute("SELECT remaining FROM balances WHERE tenant=? AND order_id=?",
                                 (tenant, order)).fetchone()
            if balance is None or amount > balance[0]:
                raise ValueError("订单不存在或超过剩余可退金额")
            receipt = {"operation_id": operation_id, "order_id": order,
                       "amount_minor": amount, "currency": currency,
                       "receipt_id": "demo-" + operation_id, "status": "refunded"}
            db.execute("UPDATE balances SET remaining=remaining-? WHERE tenant=? AND order_id=?",
                       (amount, tenant, order))
            db.execute("INSERT INTO receipts VALUES (?, ?, ?)",
                       (operation_id, fp, json.dumps(receipt)))
        # with 已提交支付事务。此处抛错故意制造“远端成功、调用方未知”。
        if lose_response:
            raise TimeoutError("教学故障：支付已提交，但响应丢失")
        return receipt
