from __future__ import annotations

from datetime import UTC, datetime, timedelta
from typing import Any

from sqlalchemy import or_, select

from app.core.logging import get_logger
from app.models.financial_record import ExpenseClaim
from app.services.expense_claim_constants import (
    AI_REVIEW_LOOKBACK_DAYS,
    AI_REVIEW_REPEAT_RISK_BLOCK_COUNT,
    AI_REVIEW_REPEAT_RISK_WARNING_COUNT,
)
from app.services.expense_claim_item_sync import ExpenseClaimItemSyncMixin
from app.services.expense_claim_platform_risk import ExpenseClaimPlatformRiskMixin
from app.services.expense_claim_policy_review import ExpenseClaimPolicyReviewMixin
from app.services.expense_claim_risk_flags import (
    dedupe_claim_risk_flags,
    is_blocking_claim_risk_flag,
)
from app.services.expense_claim_risk_stage import with_risk_business_stage
from app.services.expense_claim_tenant_scope import ExpenseClaimTenantScopeMixin
from app.services.risk_observations import RiskObservationService

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


class ExpenseClaimRiskReviewMixin(
    ExpenseClaimPlatformRiskMixin,
    ExpenseClaimPolicyReviewMixin,
    ExpenseClaimItemSyncMixin,
):
    def _run_ai_submission_review(self, claim: ExpenseClaim) -> dict[str, Any]:
        base_flags = list(claim.risk_flags_json or [])
        preserved_flags = [
            flag
            for flag in base_flags
            if not (
                isinstance(flag, dict)
                and str(flag.get("source") or "").strip() == "submission_review"
            )
        ]

        review_flags: list[dict[str, Any]] = []

        manager_name = self._resolve_claim_manager_name(claim)
        if not manager_name:
            review_flags.append(
                {
                    "source": "submission_review",
                    "severity": "medium",
                    "label": "审批链待分配",
                    "message": "自动检测发现直属领导缺失，已提交到审批环节等待分配或复核。",
                }
            )

        historical_risk_count = self._count_recent_risky_claims(claim)
        if historical_risk_count >= AI_REVIEW_REPEAT_RISK_BLOCK_COUNT:
            review_flags.append(
                {
                    "source": "submission_review",
                    "severity": "medium",
                    "label": "历史风险偏高",
                    "message": (
                        f"近 {AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 "
                        f"{historical_risk_count} 笔带风险标记的报销，"
                        "本次已追加到审批链重点关注。"
                    ),
                }
            )
        elif historical_risk_count >= AI_REVIEW_REPEAT_RISK_WARNING_COUNT:
            review_flags.append(
                {
                    "source": "submission_review",
                    "severity": "low",
                    "label": "历史风险提醒",
                    "message": (
                        f"近 {AI_REVIEW_LOOKBACK_DAYS} 天内该员工已有 "
                        f"{historical_risk_count} 笔带风险标记的报销，"
                        "建议直属领导重点复核。"
                    ),
                }
            )

        travel_review = self._run_travel_policy_review(claim)
        review_flags.extend(travel_review["flags"])

        scene_policy_review = self._run_scene_policy_review(claim)
        review_flags.extend(scene_policy_review["flags"])

        platform_risk_review = self.evaluate_platform_risk_rules(
            claim,
            business_stage="reimbursement",
        )
        platform_risk_flags = list(platform_risk_review["flags"])
        review_flags.extend(platform_risk_flags)
        if platform_risk_flags:
            try:
                RiskObservationService(self.db).upsert_platform_risk_flags(
                    claim,
                    platform_risk_flags,
                )
            except Exception as error:
                logger.exception(
                    "Failed to persist platform risk observations for claim_id=%s",
                    claim.id,
                )
                if any(is_blocking_claim_risk_flag(flag) for flag in platform_risk_flags):
                    raise RuntimeError(
                        "高风险观察持久化失败，已停止提交；请稍后重试或联系管理员。"
                    ) from error

        review_flags = [with_risk_business_stage(flag, "reimbursement") for flag in review_flags]
        final_risk_flags = dedupe_claim_risk_flags([*preserved_flags, *review_flags])

        return {
            "status": "submitted",
            "approval_stage": "直属领导审批",
            "risk_flags": final_risk_flags,
            "rule_set_fingerprint": str(platform_risk_review.get("rule_set_fingerprint") or ""),
            "message": (
                f"报销单 {claim.claim_no} 已完成自动检测，"
                f"现已提交给直属领导 {manager_name or '审批人'} 审批。"
            ),
            "passed": True,
        }

    @staticmethod
    def _resolve_claim_manager_name(claim: ExpenseClaim) -> str:
        if claim.employee is not None:
            if claim.employee.manager is not None and claim.employee.manager.name:
                return str(claim.employee.manager.name).strip()
            if (
                claim.employee.organization_unit is not None
                and claim.employee.organization_unit.manager_name
            ):
                return str(claim.employee.organization_unit.manager_name).strip()
        return ""

    def _count_recent_risky_claims(self, claim: ExpenseClaim) -> int:
        filters = []
        if claim.employee_id:
            filters.append(ExpenseClaim.employee_id == claim.employee_id)
        elif claim.employee_name:
            filters.append(ExpenseClaim.employee_name == claim.employee_name)
        if not filters:
            return 0

        since = datetime.now(UTC) - timedelta(days=AI_REVIEW_LOOKBACK_DAYS)
        stmt = (
            select(ExpenseClaim)
            .where(or_(*filters))
            .where(ExpenseClaim.id != claim.id)
            .where(ExpenseClaim.occurred_at >= since)
            .where(
                ExpenseClaimTenantScopeMixin.build_claim_tenant_condition(
                    ExpenseClaimTenantScopeMixin.resolve_claim_tenant_id(
                        self.db,
                        claim.id,
                    )
                )
            )
        )
        recent_claims = list(self.db.scalars(stmt).all())
        return sum(1 for item in recent_claims if list(item.risk_flags_json or []))
