from __future__ import annotations

import hashlib
import json
import uuid
from decimal import Decimal

from sqlalchemy import select
from sqlalchemy.orm import Session

from app.api.deps import CurrentUserContext
from app.models.expense_case import BusinessEvent, ExpenseCase
from app.models.financial_record import ExpenseClaim
from app.schemas.user_agent import UserAgentRequest
from app.services.expense_cases import ExpenseCaseService


class ExpenseApplicationDraftEventService:
    """为 AI 申请草稿生成稳定、租户隔离的费用事件。"""

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

    def record(
        self,
        payload: UserAgentRequest,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
        *,
        event_type: str,
        previous_status: str = "",
        previous_approval_stage: str = "",
    ) -> tuple[ExpenseCase, BusinessEvent]:
        return ExpenseCaseService(self.db).record_claim_event(
            claim,
            event_type=event_type,
            actor_id=current_user.username,
            tenant_id=current_user.tenant_id,
            correlation_id=payload.run_id,
            idempotency_key=self._build_idempotency_key(
                payload,
                claim,
                current_user,
                event_type=event_type,
            ),
            previous_status=previous_status,
            previous_approval_stage=previous_approval_stage,
        )

    def prepare_created_draft(
        self,
        payload: UserAgentRequest,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
    ) -> tuple[str, ExpenseClaim | None]:
        """生成数据库级稳定聚合 ID，并查找同一动作已创建的草稿。"""

        idempotency_key = self._build_idempotency_key(
            payload,
            claim,
            current_user,
            event_type="claim_draft_created",
        )
        claim.id = str(uuid.uuid5(uuid.NAMESPACE_URL, idempotency_key))
        existing = self.find_created_draft(current_user, idempotency_key=idempotency_key)
        if existing is not None:
            return idempotency_key, existing

        if self.db.get(ExpenseClaim, claim.id) is not None:
            raise RuntimeError("申请草稿幂等状态不完整，请重试。")
        return idempotency_key, None

    def find_created_draft(
        self,
        current_user: CurrentUserContext,
        *,
        idempotency_key: str,
    ) -> ExpenseClaim | None:
        event = self.db.scalar(
            select(BusinessEvent)
            .where(
                BusinessEvent.tenant_id == current_user.tenant_id,
                BusinessEvent.aggregate_type == "expense_claim",
                BusinessEvent.event_type == "claim_draft_created",
                BusinessEvent.idempotency_key == idempotency_key,
                BusinessEvent.actor_id == current_user.username,
            )
            .order_by(BusinessEvent.occurred_at.desc())
            .limit(1)
        )
        if event is None:
            return None
        return self.db.get(ExpenseClaim, event.aggregate_id)

    @staticmethod
    def _build_idempotency_key(
        payload: UserAgentRequest,
        claim: ExpenseClaim,
        current_user: CurrentUserContext,
        *,
        event_type: str,
    ) -> str:
        snapshot = {
            "tenant_id": str(current_user.tenant_id or "default"),
            "actor_id": str(current_user.username or "anonymous"),
            "run_id": str(payload.run_id or "application-draft"),
            "expense_type": str(claim.expense_type or ""),
            "reason": str(claim.reason or ""),
            "location": str(claim.location or ""),
            "amount": str(claim.amount or Decimal("0.00")),
            "currency": str(claim.currency or "CNY"),
            "status": str(claim.status or ""),
            "approval_stage": str(claim.approval_stage or ""),
            "risk_flags": claim.risk_flags_json or [],
        }
        serialized = json.dumps(
            snapshot,
            ensure_ascii=False,
            sort_keys=True,
            separators=(",", ":"),
            default=str,
        )
        digest = hashlib.sha256(serialized.encode("utf-8")).hexdigest()
        return f"application-draft:{event_type}:{digest}"
