from __future__ import annotations

import json
import re
from datetime import UTC, datetime, timedelta
from decimal import Decimal, InvalidOperation
from typing import Any

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

from app.api.deps import CurrentUserContext
from app.core.agent_enums import AgentAssetStatus, AgentAssetType
from app.models.employee import Employee
from app.models.financial_record import ExpenseClaim
from app.schemas.agent_asset import AgentAssetListItem
from app.schemas.reimbursement import TravelReimbursementCalculatorRequest
from app.schemas.user_agent import (
    UserAgentCitation,
    UserAgentDraftPayload,
    UserAgentExpenseQueryRecord,
    UserAgentQueryPayload,
    UserAgentQueryStatusGroup,
    UserAgentReviewAction,
    UserAgentReviewEditField,
    UserAgentReviewClaimGroup,
    UserAgentReviewDocumentCard,
    UserAgentReviewDocumentField,
    UserAgentReviewPayload,
    UserAgentReviewRiskBrief,
    UserAgentReviewSlotCard,
    UserAgentRequest,
    UserAgentResponse,
    UserAgentSuggestedAction,
)
from app.services.agent_assets import AgentAssetService
from app.services.agent_foundation import AgentFoundationService
from app.services.expense_claims import ExpenseClaimService
from app.services.expense_rule_runtime import ExpenseRuleRuntimeService, RuntimeTravelPolicy, resolve_document_type_label
from app.services.risk_ontology_bridge import resolve_rule_codes_for_risk_check
from app.services.runtime_chat import RuntimeChatService
from app.services.runtime_chat_commercial import build_runtime_chat_commercial_observer
from app.services.travel_reimbursement_calculator import TravelReimbursementCalculatorService
from app.services.user_agent_documents import UserAgentDocumentService
from app.services.user_agent_knowledge import UserAgentKnowledgeMixin

from app.services.user_agent_constants import *
from app.services.user_agent_application import UserAgentApplicationMixin
from app.services.user_agent_response import UserAgentResponseMixin
from app.services.user_agent_review_core import UserAgentReviewCoreMixin
from app.services.user_agent_review_messages import UserAgentReviewMessageMixin
from app.services.user_agent_review_profile import UserAgentReviewProfileMixin
from app.services.user_agent_review_slots import UserAgentReviewSlotMixin
from app.services.user_agent_review_travel_policy import UserAgentReviewTravelPolicyMixin
from app.services.user_agent_review_travel_receipts import UserAgentReviewTravelReceiptMixin


class UserAgentService(
    UserAgentResponseMixin,
    UserAgentApplicationMixin,
    UserAgentKnowledgeMixin,
    UserAgentReviewCoreMixin,
    UserAgentReviewTravelPolicyMixin,
    UserAgentReviewTravelReceiptMixin,
    UserAgentReviewMessageMixin,
    UserAgentReviewProfileMixin,
    UserAgentReviewSlotMixin,
):
    def __init__(self, db: Session) -> None:
        self.db = db
        self.asset_service = AgentAssetService(db)
        self.runtime_chat_service = RuntimeChatService(
            db,
            attempt_observer=build_runtime_chat_commercial_observer(db),
        )
        self._document_service = UserAgentDocumentService(group_scene_labels=GROUP_SCENE_LABELS)

    def respond(self, payload: UserAgentRequest) -> UserAgentResponse:
        AgentFoundationService(self.db).ensure_foundation_ready()
        citations = self._build_citations(payload)
        risk_flags = self._resolve_risk_flags(payload)
        if self._is_expense_application_request(payload):
            return self._build_expense_application_response(
                payload,
                risk_flags=risk_flags,
            )
        suggested_actions = self._build_suggested_actions(payload)
        if self._should_prompt_expense_scene_selection(payload):
            return UserAgentResponse(
                answer=self._build_expense_scene_selection_answer(payload),
                citations=citations,
                suggested_actions=suggested_actions,
                query_payload=None,
                draft_payload=None,
                review_payload=None,
                risk_flags=[],
                requires_confirmation=False,
            )
        query_payload = self._build_query_payload(payload)
        draft_payload = (
            self._build_draft_payload(payload)
            if self._should_build_draft_payload(payload)
            else None
        )
        review_payload = self._build_review_payload(
            payload,
            citations=citations,
            draft_payload=draft_payload,
        )
        review_answer = self._build_review_body_answer(
            payload,
            review_payload=review_payload,
            draft_payload=draft_payload,
        )

        if payload.degraded and payload.tool_payload.get("message"):
            return UserAgentResponse(
                answer=review_answer or str(payload.tool_payload["message"]),
                citations=citations,
                suggested_actions=suggested_actions,
                query_payload=query_payload,
                draft_payload=draft_payload,
                review_payload=review_payload,
                risk_flags=risk_flags,
                requires_confirmation=payload.requires_confirmation,
            )

        if review_answer:
            return UserAgentResponse(
                answer=review_answer,
                citations=citations,
                suggested_actions=suggested_actions,
                query_payload=query_payload,
                draft_payload=draft_payload,
                review_payload=review_payload,
                risk_flags=risk_flags,
                requires_confirmation=payload.requires_confirmation,
            )

        guided_answer = None
        if draft_payload is None or draft_payload.claim_id is None:
            guided_answer = self._build_guided_answer(payload)
        if guided_answer:
            return UserAgentResponse(
                answer=guided_answer,
                citations=citations,
                suggested_actions=suggested_actions,
                query_payload=query_payload,
                draft_payload=draft_payload,
                review_payload=review_payload,
                risk_flags=risk_flags,
                requires_confirmation=payload.requires_confirmation,
            )

        # 知识库问答必须优先让模型基于召回证据组织答案，避免片段渲染抢答导致答非所问。
        fallback_answer = self._build_fallback_answer(
            payload,
            citations=citations,
            draft_payload=draft_payload,
        )
        answer = None
        if not self._should_skip_model_answer(payload, review_payload):
            answer = self._generate_answer_with_model(
                payload,
                citations=citations,
                suggested_actions=suggested_actions,
                risk_flags=risk_flags,
                draft_payload=draft_payload,
                fallback_answer=fallback_answer,
            )

        return UserAgentResponse(
            answer=answer or fallback_answer,
            citations=citations,
            suggested_actions=suggested_actions,
            query_payload=query_payload,
            draft_payload=draft_payload,
            review_payload=review_payload,
            risk_flags=risk_flags,
            requires_confirmation=payload.requires_confirmation,
        )

    def _classify_document(
        self,
        item: dict[str, object],
        payload: UserAgentRequest,
    ) -> dict[str, str]:
        entity_values = self._collect_entity_values(payload)
        return self._document_service.classify_document(
            item,
            expense_type_code=entity_values.get("expense_type_code", ""),
            has_customer=bool(entity_values.get("customer")),
        )

    @staticmethod
    def _normalize_group_code(expense_type_code: str) -> str:
        return UserAgentDocumentService.normalize_group_code(expense_type_code)

    def _extract_document_fields(self, item: dict[str, object]) -> dict[str, str]:
        return self._document_service.extract_document_fields(item)

    @staticmethod
    def _resolve_document_time_display_label(
        *,
        document_type: str,
        key: str,
        label: str,
        normalized_label: str,
    ) -> str:
        return UserAgentDocumentService.resolve_document_time_display_label(
            document_type=document_type,
            key=key,
            label=label,
            normalized_label=normalized_label,
        )

    @staticmethod
    def _normalize_document_field_label(*, key: str, label: str) -> str:
        return UserAgentDocumentService.normalize_document_field_label(key=key, label=label)

    def _normalize_document_field_value(self, *, label: str, value: str) -> str:
        return self._document_service.normalize_document_field_value(label=label, value=value)

    def _extract_amount_text_from_value(self, value: str) -> str:
        return self._document_service.extract_amount_text_from_value(value)

    def _extract_document_merchant_name(self, item: dict[str, object]) -> str:
        return self._document_service.extract_document_merchant_name(item)

    @staticmethod
    def _is_hotel_document_item(item: dict[str, object]) -> bool:
        return UserAgentDocumentService.is_hotel_document_item(item)

    @staticmethod
    def _extract_document_merchant_name_from_text(text: str) -> str:
        return UserAgentDocumentService.extract_document_merchant_name_from_text(text)

    @staticmethod
    def _extract_amount_from_card(card: UserAgentReviewDocumentCard) -> float:
        return UserAgentDocumentService.extract_amount_from_card(card)

    def _resolve_amount_value(self, payload: UserAgentRequest) -> float:
        return self._document_service.resolve_amount_value(payload)

    def _sum_ocr_amounts(self, ocr_documents: list[dict[str, object]]) -> float:
        return self._document_service.sum_ocr_amounts(ocr_documents)

    def _infer_expense_type_from_documents(
        self,
        payload: UserAgentRequest,
        ocr_documents: list[dict[str, object]],
    ) -> str:
        entity_values = self._collect_entity_values(payload)
        return self._document_service.infer_expense_type_from_documents(
            ocr_documents,
            expense_type_code=entity_values.get("expense_type_code", ""),
            has_customer=bool(entity_values.get("customer")),
        )
