"""把报销规则执行结果安全写入 Agent 资产发布遥测。"""

from __future__ import annotations

from typing import Any, Literal

from sqlalchemy.engine import Connection, Engine
from sqlalchemy.orm import Session, sessionmaker

from app.core.logging import get_logger
from app.models.financial_record import ExpenseClaim
from app.services.agent_asset_release_telemetry import (
    AgentAssetReleaseTelemetryService,
    ReleaseTelemetryStaleRelease,
)

logger = get_logger("app.services.expense_claim_release_telemetry")

SUPPORTED_PLATFORM_RISK_EVALUATORS = frozenset(
    {
        "cross_year_invoice",
        "document_expense_mismatch",
        "duplicate_invoice",
        "entertainment_reason_missing",
        "identity_consistency",
        "location_consistency",
        "multi_city_reason_required",
        "reason_too_brief",
        "release_guard_integrity_failure",
        "template_rule",
        "vague_goods_description",
        "void_or_red_invoice",
    }
)


class ExpenseClaimReleaseTelemetryRecorder:
    """只记录当前候选路由；遥测故障不改变报销风险结论。"""

    def __init__(self, db: Session) -> None:
        self.db = db

    @staticmethod
    def manifest_key(manifest: dict[str, Any]) -> tuple[str, str]:
        return (
            str(manifest.get("_rule_asset_id") or "").strip(),
            str(manifest.get("rule_code") or "").strip(),
        )

    @staticmethod
    def baseline_hit(
        manifest: dict[str, Any],
        *,
        enforced_outcomes: dict[tuple[str, str], bool],
        release_key: tuple[str, str],
    ) -> bool | None:
        if (
            str(manifest.get("_release_stage") or "").strip().lower() == "shadow"
            and str(manifest.get("_release_mode") or "").strip().lower() == "shadow"
        ):
            return enforced_outcomes.get(release_key)
        return None

    def record(
        self,
        *,
        tenant_id: str,
        claim: ExpenseClaim,
        manifest: dict[str, Any],
        hit: bool,
        baseline_hit: bool | None,
        runtime_status: Literal["completed", "failed"] = "completed",
        failure_code: str = "none",
        business_stage: str,
    ) -> None:
        stage = str(manifest.get("_release_stage") or "").strip().lower()
        mode = str(manifest.get("_release_mode") or "").strip().lower()
        integrity_failure = (
            str(manifest.get("evaluator") or "").strip().lower()
            == "release_guard_integrity_failure"
        )
        if stage not in {"shadow", "canary", "active"} or not str(
            manifest.get("_rule_asset_id") or ""
        ).strip():
            return
        if stage == "shadow" and mode != "shadow" and not integrity_failure:
            return
        telemetry_manifest = manifest
        if integrity_failure:
            integrity = manifest.get("_release_integrity_error")
            candidate_version = str(
                (integrity if isinstance(integrity, dict) else {}).get("candidate_version")
                or ""
            ).strip()
            if not candidate_version:
                return
            telemetry_manifest = {
                **manifest,
                "_rule_version": candidate_version,
                "_release_mode": "shadow" if stage == "shadow" else "enforced",
            }
        try:
            AgentAssetReleaseTelemetryService(self.db).record_manifest_evaluation(
                tenant_id=tenant_id,
                claim_id=str(claim.id or claim.claim_no or "").strip(),
                manifest=telemetry_manifest,
                hit=hit,
                baseline_hit=baseline_hit,
                runtime_status=runtime_status,
                failure_code=failure_code,
                business_stage=business_stage,
            )
        except ReleaseTelemetryStaleRelease:
            # Canary 未命中路由使用稳定版本，不属于候选样本。
            return
        except Exception:
            # 发布门禁会保持 collecting，不能因为监控旁路故障改变报销结论。
            logger.exception(
                "Failed to persist release telemetry tenant=%s asset=%s stage=%s",
                tenant_id,
                manifest.get("_rule_asset_id"),
                stage,
            )

    def record_failure_durably(
        self,
        *,
        tenant_id: str,
        claim: ExpenseClaim,
        manifest: dict[str, Any],
        failure_code: str,
        business_stage: str,
    ) -> None:
        """把 evaluator 崩溃样本写入独立事务，避免随报销请求一起回滚。"""

        bind = self.db.get_bind()
        if bind is None or bind.dialect.name != "postgresql":
            # SQLite 仅用于本地单元测试，无法在同一连接上可靠模拟两个并行事务。
            self.record(
                tenant_id=tenant_id,
                claim=claim,
                manifest=manifest,
                hit=False,
                baseline_hit=None,
                runtime_status="failed",
                failure_code=failure_code,
                business_stage=business_stage,
            )
            return

        engine: Engine = bind.engine if isinstance(bind, Connection) else bind
        factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
        try:
            with factory() as durable_db:
                AgentAssetReleaseTelemetryService(durable_db).record_manifest_evaluation(
                    tenant_id=tenant_id,
                    claim_id=str(claim.id or claim.claim_no or "").strip(),
                    manifest=manifest,
                    hit=False,
                    baseline_hit=None,
                    runtime_status="failed",
                    failure_code=failure_code,
                    business_stage=business_stage,
                )
                durable_db.commit()
        except ReleaseTelemetryStaleRelease:
            # 阶段已经推进时，旧失败样本不能污染新 release。
            return
        except Exception:
            # 不用遥测旁路异常替换真正的 evaluator 异常，但必须留下可观测日志。
            logger.exception(
                "Failed to durably persist release failure tenant=%s asset=%s stage=%s",
                tenant_id,
                manifest.get("_rule_asset_id"),
                manifest.get("_release_stage"),
            )
