from __future__ import annotations

from datetime import UTC, datetime
from decimal import Decimal

from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from sqlalchemy.pool import StaticPool

from app.db.base import Base
from app.models.financial_record import ExpenseClaim
from app.services.hermes_risk_clue_collector import HermesRiskClueCollectorService
from app.services.risk_observations import RiskObservationService


def build_session_factory() -> sessionmaker[Session]:
    engine = create_engine(
        "sqlite+pysqlite:///:memory:",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    Base.metadata.create_all(bind=engine)
    return sessionmaker(bind=engine, autoflush=False, autocommit=False)


def test_risk_clue_collector_outputs_review_packet_without_rule_writes() -> None:
    forbidden_rule_execution = "执行" + "规则"
    session_factory = build_session_factory()
    with session_factory() as db:
        claim = ExpenseClaim(
            id="claim-risk-clue-1",
            claim_no="RE-20260531090000-ABCDEFGH",
            employee_name="张三",
            department_name="销售部",
            expense_type="travel",
            reason="客户现场支持",
            location="上海",
            amount=Decimal("9800.00"),
            currency="CNY",
            invoice_count=2,
            occurred_at=datetime(2026, 5, 30, 9, 0, tzinfo=UTC),
            submitted_at=datetime(2026, 5, 31, 9, 0, tzinfo=UTC),
            status="submitted",
            approval_stage="财务审批",
            risk_flags_json=[
                {
                    "source": "rule_center",
                    "rule_code": "risk.travel.large_without_preapproval",
                    "label": "大额差旅缺少事前申请",
                    "message": "报销金额较高，未找到对应事前申请。",
                    "severity": "high",
                }
            ],
        )
        db.add(claim)
        db.flush()

        RiskObservationService(db).upsert_observation(
            {
                "observation_key": "risk:claim-risk-clue-1:large_without_preapproval",
                "subject_type": "expense_claim",
                "subject_key": "claim:claim-risk-clue-1",
                "subject_label": claim.claim_no,
                "claim_id": claim.id,
                "claim_no": claim.claim_no,
                "risk_type": "preapproval_absent",
                "risk_signal": "preapproval_absent",
                "title": "大额差旅缺少事前申请",
                "description": "报销金额较高，暂未匹配到事前申请，需要人工复核。",
                "risk_score": 86,
                "risk_level": "high",
                "confidence_score": 0.82,
                "source": "rule_center",
                "contribution_scores": {"S_rule": 86},
                "evidence": [
                    {
                        "source": "rule_center",
                        "title": "规则命中",
                        "detail": "金额 9800 元，缺少事前申请。",
                    }
                ],
                "policy_refs": ["risk.travel.large_without_preapproval"],
            }
        )
        db.commit()

        packet = HermesRiskClueCollectorService(db).collect_risk_clues(run_id="run-risk-clue")

        assert packet["task_type"] == "risk_clue_collect"
        assert packet["writes_rules"] is False
        assert packet["human_review_required"] is True
        assert "主流程由外层智能体执行" in packet["role_boundary"]
        assert forbidden_rule_execution not in packet["role_boundary"]
        assert packet["fact_count"] == 1
        assert packet["rule_hit_count"] >= 1
        assert packet["risk_clue_count"] >= 1
        assert packet["facts"][0]["claim_kind"] == "reimbursement"
        assert packet["risk_clues"][0]["status"] == "human_review_required"
        assert packet["risk_clues"][0]["observation_key"]
        assert packet["risk_clues"][0]["feedback_status"] == "unreviewed"
        assert packet["risk_clues"][0]["next_action"]
        assert "recent" in packet["feedback_summary"]
        assert packet["risk_clues"][0]["not_final_conclusion"] is True
        serialized = str(packet)
        assert "auto_publish" not in serialized
        assert "candidate_risk_rules" not in serialized
