from __future__ import annotations

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext
from app.models.financial_record import ExpenseClaim
from app.models.risk_observation import RiskObservation
from app.services.expense_claim_access_policy import ExpenseClaimAccessPolicy

RISK_POOL_ROLE_CODES = {
    "budget_monitor",
    "executive",
    "finance",
}


class RiskObservationAccessPolicy:
    """集中定义风险池、单据风险和处置动作的访问边界。"""

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

    def can_read_tenant_pool(self, current_user: CurrentUserContext) -> bool:
        if current_user.is_admin:
            return True
        return bool(self.claim_policy.normalize_role_codes(current_user) & RISK_POOL_ROLE_CODES)

    def find_visible_claim(
        self,
        claim_id: str,
        current_user: CurrentUserContext,
    ) -> ExpenseClaim | None:
        statement = select(ExpenseClaim).where(ExpenseClaim.id == claim_id)
        statement = self.claim_policy.apply_claim_scope(
            statement,
            current_user,
            include_approval_scope=True,
        )
        return self.db.scalar(statement)

    def can_read_claim_risks(
        self,
        claim_id: str,
        current_user: CurrentUserContext,
    ) -> bool:
        claim = self.find_visible_claim(claim_id, current_user)
        if claim is None:
            return False
        if current_user.is_admin:
            return True
        # 完整证据链含图谱、相似案例与人工反馈，仅当前审批人可读取。
        return self.claim_policy.can_approve_claim(current_user, claim)

    def can_manage_disposition(
        self,
        observation: RiskObservation,
        current_user: CurrentUserContext,
    ) -> bool:
        claim_id = str(observation.claim_id or "").strip()
        claim = self.find_visible_claim(claim_id, current_user) if claim_id else None
        return self.can_manage_locked_disposition(
            observation,
            current_user,
            locked_claim=claim,
        )

    def can_manage_locked_disposition(
        self,
        observation: RiskObservation,
        current_user: CurrentUserContext,
        *,
        locked_claim: ExpenseClaim | None,
    ) -> bool:
        """在共享 Claim 行锁内按最新审批节点复核处置权限。"""

        if current_user.is_admin:
            return True
        claim_id = str(observation.claim_id or "").strip()
        if not claim_id or locked_claim is None or str(locked_claim.id) != claim_id:
            # 无关联单据的租户级风险缺少可复用的业务权限边界，仅 admin 可处置。
            return False
        return self.claim_policy.can_approve_claim(current_user, locked_claim)
