from __future__ import annotations

import json
from datetime import datetime, timezone
from typing import Any

from sqlalchemy import or_, select
from sqlalchemy.orm import Session

from app.core.logging import get_logger
from app.models.financial_record import ExpenseClaim
from app.models.hermes_config import HermesTaskExecutionLog
from app.models.hermes_report import HermesRiskReport
from app.services.runtime_chat import RuntimeChatService

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

class HermesRiskScannerService:
    def __init__(self, db: Session) -> None:
        self.db = db
        self.chat_service = RuntimeChatService(db)

    def scan_global_risks(self, log_id: str | None = None) -> None:
        logger.info("Starting global risk scan for Hermes...")
        
        # 1. Fetch unscanned claims
        claims = self._fetch_unscanned_claims()
        if not claims:
            logger.info("No unscanned claims found. Aborting scan.")
            return

        logger.info(f"Fetched {len(claims)} claims to analyze.")
        
        # 2. Extract context for LLM
        claims_context = []
        for c in claims:
            claims_context.append({
                "claim_id": c.id,
                "claim_no": c.claim_no,
                "employee_name": c.employee_name,
                "department_name": c.department_name,
                "expense_type": c.expense_type,
                "location": c.location,
                "amount": float(c.amount),
                "occurred_at": str(c.occurred_at) if c.occurred_at else None,
                "reason": c.reason,
            })
            
        # 3. Analyze with LLM
        risk_results = self._analyze_claims_with_llm(claims_context)
        
        # 4. Process and persist results
        detected_risk_count = 0
        if risk_results:
            for risk in risk_results:
                claim_ids = risk.get("claim_ids", [])
                if not claim_ids:
                    continue
                    
                detected_risk_count += 1
                for cid in claim_ids:
                    report = HermesRiskReport(
                        claim_id=cid,
                        execution_log_id=log_id,
                        risk_level=risk.get("risk_level", "medium"),
                        risk_type=risk.get("risk_type", "unknown"),
                        risk_description=risk.get("description", "No description provided"),
                        related_claim_ids=claim_ids,
                    )
                    self.db.add(report)
                    
                    # Update claim flags
                    claim_obj = next((c for c in claims if c.id == cid), None)
                    if claim_obj:
                        claim_obj.hermes_risk_flag = True

        # 5. Mark all as scanned
        now = datetime.now(timezone.utc)
        for c in claims:
            c.hermes_scanned_at = now
            
        self.db.commit()
        logger.info(f"Hermes risk scan completed. Found {detected_risk_count} risks.")
        
    def _fetch_unscanned_claims(self) -> list[ExpenseClaim]:
        stmt = select(ExpenseClaim).where(
            ExpenseClaim.status.in_(["draft", "submitted", "review"]),
            or_(
                ExpenseClaim.hermes_scanned_at.is_(None),
                ExpenseClaim.hermes_risk_flag.is_(False) # only rescan if it has no flags yet
            )
        ).limit(50) # Batch size to prevent Token overflow
        
        return list(self.db.scalars(stmt).all())
        
    def _analyze_claims_with_llm(self, claims_context: list[dict[str, Any]]) -> list[dict[str, Any]]:
        system_prompt = (
            "你是 X-Financial 的 Hermes 内控审计智能体。请分析以下近期的报销单数据集合，寻找以下潜在风险：\n"
            "1. 拆单行为 (split_billing)：同一人在相邻日期针对同一类目/商户提交多笔恰好贴近免审额度的小额单据。\n"
            "2. 群体合谋 (collusion)：不同部门的员工在同一天去同一家非标准酒店类偏僻商户高额消费。\n"
            "3. 异常频次 (frequency_anomaly)：某员工在短时间内的打车或招待频次极度不合理。\n"
            "请严格以 JSON 数组格式返回结果，如果没有风险返回空数组 `[]`。\n"
            "JSON 格式要求：\n"
            "[\n"
            "  {\n"
            '    "risk_type": "split_billing",\n'
            '    "risk_level": "high",\n'
            '    "claim_ids": ["uuid-1", "uuid-2"],\n'
            '    "description": "详细推理过程，为什么判定为拆单。"\n'
            "  }\n"
            "]\n"
        )
        
        messages = [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": json.dumps(claims_context, ensure_ascii=False, indent=2)}
        ]
        
        response_text = self.chat_service.complete(
            messages,
            max_tokens=1500,
            temperature=0.1
        )
        
        if not response_text:
            logger.warning("LLM returned empty response for risk scan.")
            return []
            
        # Clean markdown formatting if present
        cleaned_text = response_text.replace("```json", "").replace("```", "").strip()
        try:
            return json.loads(cleaned_text)
        except json.JSONDecodeError as e:
            logger.error(f"Failed to parse LLM risk scan response as JSON: {e}\nResponse: {response_text}")
            return []
