"""
Mid Platform schemas.
[AC-IDMP-01, AC-IDMP-02, AC-IDMP-07, AC-IDMP-11, AC-IDMP-12, AC-IDMP-15, AC-IDMP-17, AC-IDMP-18, AC-IDMP-19, AC-IDMP-20]
Aligned with spec/intent-driven-mid-platform/openapi.provider.yaml
"""

from __future__ import annotations

import uuid
from enum import Enum
from typing import Any

from pydantic import BaseModel, Field


class ExecutionMode(str, Enum):
    """[AC-IDMP-02] Execution mode for dialogue response."""
    AGENT = "agent"
    MICRO_FLOW = "micro_flow"
    FIXED = "fixed"
    TRANSFER = "transfer"


class HighRiskScenario(str, Enum):
    """[AC-IDMP-20] High risk scenario types for mandatory takeover."""
    REFUND = "refund"
    COMPLAINT_ESCALATION = "complaint_escalation"
    PRIVACY_SENSITIVE_PROMISE = "privacy_sensitive_promise"
    TRANSFER = "transfer"


class ToolCallStatus(str, Enum):
    """[AC-IDMP-15] Tool call status."""
    OK = "ok"
    TIMEOUT = "timeout"
    ERROR = "error"
    REJECTED = "rejected"


class ToolType(str, Enum):
    """[AC-IDMP-19] Tool type for registry governance."""
    INTERNAL = "internal"
    MCP = "mcp"


class SessionMode(str, Enum):
    """[AC-IDMP-09] Session mode for bot/human switching."""
    BOT_ACTIVE = "BOT_ACTIVE"
    HUMAN_ACTIVE = "HUMAN_ACTIVE"


class HistoryMessage(BaseModel):
    """[AC-IDMP-03] History message with only delivered content."""
    role: str = Field(..., description="Message role: user, assistant, or human")
    content: str = Field(..., description="Message content")


class InterruptedSegment(BaseModel):
    """[AC-IDMP-04] Interrupted segment for handling user interruption."""
    segment_id: str = Field(..., description="Segment ID")
    content: str = Field(..., description="Segment content")


class FeatureFlags(BaseModel):
    """[AC-IDMP-17] Feature flags for session-level grayscale and rollback."""
    agent_enabled: bool | None = Field(default=True, description="Session-level Agent grayscale switch")
    rollback_to_legacy: bool | None = Field(default=False, description="Force rollback to legacy pipeline")


class HumanizeConfigRequest(BaseModel):
    """[AC-MARH-11] 拟人化配置请求。"""
    enabled: bool | None = Field(default=True, description="Enable humanize strategy")
    min_delay_ms: int | None = Field(default=50, ge=0, description="Minimum delay in milliseconds")
    max_delay_ms: int | None = Field(default=500, ge=0, description="Maximum delay in milliseconds")
    length_bucket_strategy: str | None = Field(default="simple", description="Strategy: simple or semantic")


class DialogueRequest(BaseModel):
    """[AC-IDMP-01, AC-IDMP-03, AC-IDMP-04, AC-IDMP-17, AC-MARH-11] Dialogue request schema."""
    session_id: str = Field(..., description="Session ID for conversation tracking")
    user_id: str | None = Field(default=None, description="User ID for memory recall and update")
    user_message: str = Field(..., min_length=1, max_length=2000, description="User message content")
    history: list[HistoryMessage] = Field(default_factory=list, description="Only delivered history")
    interrupted_segments: list[InterruptedSegment] | None = Field(default=None, description="Interrupted segments")
    feature_flags: FeatureFlags | None = Field(default=None, description="Feature flags for grayscale control")
    humanize_config: HumanizeConfigRequest | None = Field(
        default=None, description="Humanize config for segment delay"
    )
    scene: str | None = Field(default=None, description="Scene identifier for KB filtering, e.g., 'open_consult', 'after_sale'")


class Segment(BaseModel):
    """[AC-IDMP-01] Response segment with delay control."""
    segment_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Segment ID")
    text: str = Field(..., description="Segment text content")
    delay_after: int = Field(default=0, ge=0, description="Delay after this segment in milliseconds")


class TimeoutProfile(BaseModel):
    """[AC-MARH-08, AC-MARH-09] Timeout configuration profile."""
    per_tool_timeout_ms: int = Field(default=30000, le=60000, description="Per-tool timeout in milliseconds")
    llm_timeout_ms: int = Field(default=60000, le=120000, description="LLM call timeout in milliseconds")
    end_to_end_timeout_ms: int = Field(default=120000, le=180000, description="End-to-end timeout in milliseconds")


class MetricsSnapshot(BaseModel):
    """[AC-IDMP-18] Runtime metrics snapshot."""
    task_completion_rate: float | None = Field(default=None, ge=0.0, le=1.0, description="Task completion rate")
    slot_completion_rate: float | None = Field(default=None, ge=0.0, le=1.0, description="Slot completion rate")
    wrong_transfer_rate: float | None = Field(default=None, ge=0.0, le=1.0, description="Wrong transfer rate")
    no_recall_rate: float | None = Field(default=None, ge=0.0, le=1.0, description="No recall rate")
    avg_latency_ms: float | None = Field(default=None, ge=0.0, description="Average latency in milliseconds")


class ToolCallTrace(BaseModel):
    """[AC-IDMP-15, AC-IDMP-19] Tool call trace for observability."""
    tool_name: str = Field(..., description="Tool name")
    tool_type: ToolType | None = Field(default=ToolType.INTERNAL, description="Tool type: internal or mcp")
    registry_version: str | None = Field(default=None, description="Tool registry version")
    auth_applied: bool | None = Field(default=False, description="Whether auth was applied")
    duration_ms: int = Field(..., ge=0, description="Duration in milliseconds")
    status: ToolCallStatus = Field(..., description="Tool call status")
    error_code: str | None = Field(default=None, description="Error code if failed")
    args_digest: str | None = Field(default=None, description="Arguments digest for logging")
    result_digest: str | None = Field(default=None, description="Result digest for logging")
    arguments: dict[str, Any] | None = Field(default=None, description="Full tool call arguments")
    result: Any = Field(default=None, description="Full tool call result")


class SegmentStats(BaseModel):
    """[AC-MARH-12] Segment statistics for humanize strategy."""
    segment_count: int = Field(default=0, ge=0, description="Number of segments")
    avg_segment_length: float = Field(default=0.0, ge=0.0, description="Average segment length")
    humanize_strategy: str | None = Field(default=None, description="Humanize strategy used")


class TraceInfo(BaseModel):
    """[AC-MARH-02, AC-MARH-03, AC-MARH-05, AC-MARH-06, AC-MARH-07, AC-MARH-11,
    AC-MARH-12, AC-MARH-18, AC-MARH-19, AC-MARH-20, AC-SCENE-SLOT-02] Trace info for observability."""
    mode: ExecutionMode = Field(..., description="Execution mode")
    intent: str | None = Field(default=None, description="Matched intent")
    request_id: str | None = Field(
        default_factory=lambda: str(uuid.uuid4()), description="Request ID"
    )
    generation_id: str | None = Field(
        default_factory=lambda: str(uuid.uuid4()),
        description="Generation ID for interrupt handling",
    )
    guardrail_triggered: bool | None = Field(default=False, description="Whether guardrail was triggered")
    guardrail_rule_id: str | None = Field(default=None, description="Guardrail rule ID that triggered")
    interrupt_consumed: bool | None = Field(default=False, description="Whether interrupted segments were consumed")
    kb_tool_called: bool | None = Field(default=False, description="Whether KB tool was called")
    kb_hit: bool | None = Field(default=False, description="Whether KB search had results")
    fallback_reason_code: str | None = Field(default=None, description="Fallback reason code")
    react_iterations: int | None = Field(default=0, ge=0, le=5, description="ReAct loop iterations")
    timeout_profile: TimeoutProfile | None = Field(default=None, description="Timeout profile")
    segment_stats: SegmentStats | None = Field(default=None, description="Segment statistics")
    metrics_snapshot: MetricsSnapshot | None = Field(default=None, description="Metrics snapshot")
    high_risk_policy_set: list[HighRiskScenario] | None = Field(default=None, description="Active high-risk policy set")
    tools_used: list[str] | None = Field(default=None, description="Tools used in this request")
    tool_calls: list[ToolCallTrace] | None = Field(default=None, description="Tool call traces")
    duration_ms: int = Field(default=0, ge=0, description="Execution duration in milliseconds")
    created_at: str | None = Field(default=None, description="Creation timestamp")
    # [AC-SCENE-SLOT-02] 场景槽位追踪字段
    scene: str | None = Field(default=None, description="当前场景标识")
    scene_slot_context: dict[str, Any] | None = Field(default=None, description="场景槽位上下文信息")
    missing_slots: list[str] | None = Field(default=None, description="缺失的必填槽位列表")
    ask_back_triggered: bool | None = Field(default=False, description="是否触发了追问")
    slot_sources: dict[str, str] | None = Field(default=None, description="槽位值来源映射")
    kb_filter_sources: dict[str, str] | None = Field(default=None, description="KB 过滤条件来源映射")
    # [Step-KB-Binding] 步骤知识库绑定追踪
    step_kb_binding: dict[str, Any] | None = Field(default=None, description="步骤知识库绑定信息，包含 step_id, allowed_kb_ids, used_kb_ids 等")


class DialogueResponse(BaseModel):
    """[AC-IDMP-01, AC-IDMP-02] Dialogue response with segments and trace."""
    segments: list[Segment] = Field(..., description="Response segments")
    trace: TraceInfo = Field(..., description="Trace info for observability")


class ReportedMessage(BaseModel):
    """[AC-IDMP-08] Reported message for message report API."""
    role: str = Field(..., description="Message role: user, assistant, human, or system")
    content: str = Field(..., description="Message content")
    source: str = Field(..., description="Message source: bot, human, or channel")
    timestamp: str = Field(..., description="Message timestamp in ISO format")
    segment_id: str | None = Field(default=None, description="Segment ID if applicable")


class MessageReportRequest(BaseModel):
    """[AC-IDMP-08] Message report request schema."""
    session_id: str = Field(..., description="Session ID")
    messages: list[ReportedMessage] = Field(..., description="Messages to report")


class SwitchModeRequest(BaseModel):
    """[AC-IDMP-09] Switch session mode request."""
    mode: SessionMode = Field(..., description="Target mode: BOT_ACTIVE or HUMAN_ACTIVE")
    reason: str | None = Field(default=None, description="Reason for mode switch")


class SwitchModeResponse(BaseModel):
    """[AC-IDMP-09] Switch session mode response."""
    session_id: str = Field(..., description="Session ID")
    mode: SessionMode = Field(..., description="Current mode after switch")


class MidSessionState(BaseModel):
    """Internal session state for mid platform."""
    session_id: str
    tenant_id: str
    mode: SessionMode = SessionMode.BOT_ACTIVE
    generation_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    active_flow_id: str | None = None
    context: dict[str, Any] | None = None
    created_at: str | None = None
    updated_at: str | None = None


class PolicyRouterResult(BaseModel):
    """[AC-IDMP-02, AC-IDMP-05, AC-IDMP-16] Policy router decision result."""
    mode: ExecutionMode = Field(..., description="Decided execution mode")
    intent: str | None = Field(default=None, description="Matched intent")
    confidence: float | None = Field(default=None, ge=0.0, le=1.0, description="Intent confidence")
    fallback_reason_code: str | None = Field(default=None, description="Fallback reason if applicable")
    high_risk_triggered: bool = Field(default=False, description="Whether high-risk scenario triggered")
    target_flow_id: str | None = Field(default=None, description="Target flow ID for micro_flow mode")
    fixed_reply: str | None = Field(default=None, description="Fixed reply for fixed mode")
    transfer_message: str | None = Field(default=None, description="Transfer message for transfer mode")


class ReActContext(BaseModel):
    """[AC-IDMP-11] ReAct loop context for iteration control."""
    iteration: int = Field(default=0, ge=0, le=5, description="Current iteration count")
    max_iterations: int = Field(default=5, ge=3, le=5, description="Maximum iterations allowed")
    tool_calls: list[ToolCallTrace] = Field(default_factory=list, description="Tool call history")
    should_continue: bool = Field(default=True, description="Whether to continue ReAct loop")
    final_answer: str | None = Field(default=None, description="Final answer if completed")


class CreateShareRequest(BaseModel):
    """[AC-IDMP-SHARE] Request to create a shared session."""
    title: str | None = Field(default=None, max_length=255, description="Share title")
    description: str | None = Field(default=None, max_length=1000, description="Share description")
    expires_in_days: int = Field(default=7, ge=1, le=365, description="Expiration time in days")
    max_concurrent_users: int = Field(default=10, ge=1, le=100, description="Maximum concurrent users")


class ShareResponse(BaseModel):
    """[AC-IDMP-SHARE] Response after creating a share."""
    share_token: str = Field(..., description="Unique share token")
    share_url: str = Field(..., description="Full share URL")
    expires_at: str = Field(..., description="Expiration time in ISO format")
    title: str | None = Field(default=None, description="Share title")
    description: str | None = Field(default=None, description="Share description")
    max_concurrent_users: int = Field(..., description="Maximum concurrent users")


class SharedSessionInfo(BaseModel):
    """[AC-IDMP-SHARE] Information about a shared session."""
    session_id: str = Field(..., description="Session ID")
    title: str | None = Field(default=None, description="Share title")
    description: str | None = Field(default=None, description="Share description")
    expires_at: str = Field(..., description="Expiration time in ISO format")
    max_concurrent_users: int = Field(..., description="Maximum concurrent users")
    current_users: int = Field(..., description="Current online users")
    history: list[HistoryMessage] = Field(default_factory=list, description="Historical messages")


class SharedMessageRequest(BaseModel):
    """[AC-IDMP-SHARE] Request to send a message via shared session."""
    user_message: str = Field(..., min_length=1, max_length=2000, description="User message content")


class ShareListItem(BaseModel):
    """[AC-IDMP-SHARE] Share list item for listing all shares of a session."""
    share_token: str = Field(..., description="Share token")
    share_url: str = Field(..., description="Full share URL")
    title: str | None = Field(default=None, description="Share title")
    description: str | None = Field(default=None, description="Share description")
    expires_at: str = Field(..., description="Expiration time in ISO format")
    is_active: bool = Field(..., description="Whether share is active")
    max_concurrent_users: int = Field(..., description="Maximum concurrent users")
    current_users: int = Field(..., description="Current online users")
    created_at: str = Field(..., description="Creation time in ISO format")


class ShareListResponse(BaseModel):
    """[AC-IDMP-SHARE] Response for listing shares."""
    shares: list[ShareListItem] = Field(..., description="List of shares")


class KbSearchDynamicHit(BaseModel):
    """[AC-MARH-05] Single KB search hit."""
    id: str = Field(..., description="Hit ID")
    content: str = Field(..., description="Hit content")
    score: float = Field(..., ge=0.0, le=1.0, description="Relevance score")
    metadata: dict[str, Any] = Field(default_factory=dict, description="Hit metadata")


class MissingRequiredSlot(BaseModel):
    """[AC-MARH-05] Missing required slot info."""
    field_key: str = Field(..., description="Field key")
    label: str = Field(..., description="Field label")
    reason: str = Field(..., description="Missing reason")


class KbSearchDynamicResultSchema(BaseModel):
    """[AC-MARH-05, AC-MARH-06] KB dynamic search result schema."""
    success: bool = Field(..., description="Whether search succeeded")
    hits: list[KbSearchDynamicHit] = Field(default_factory=list, description="Search hits")
    applied_filter: dict[str, Any] = Field(default_factory=dict, description="Applied filter")
    missing_required_slots: list[MissingRequiredSlot] = Field(
        default_factory=list, description="Missing required slots"
    )
    filter_debug: dict[str, Any] = Field(default_factory=dict, description="Filter debug info")
    fallback_reason_code: str | None = Field(default=None, description="Fallback reason code")
    duration_ms: int = Field(default=0, ge=0, description="Duration in milliseconds")


class IntentHintOutput(BaseModel):
    """[AC-IDMP-02, AC-IDMP-16] 轻量意图提示工具输出。"""
    intent: str | None = Field(default=None, description="识别到的意图名称")
    confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="置信度 0~1")
    response_type: str | None = Field(
        default=None,
        description="响应类型: fixed|rag|flow|transfer|null"
    )
    suggested_mode: ExecutionMode | None = Field(
        default=None,
        description="建议执行模式: agent|micro_flow|fixed|transfer"
    )
    target_flow_id: str | None = Field(default=None, description="目标流程ID（flow模式）")
    target_kb_ids: list[str] | None = Field(default=None, description="目标知识库ID列表")
    fallback_reason_code: str | None = Field(default=None, description="降级原因码")
    high_risk_detected: bool = Field(default=False, description="是否检测到高风险场景")
    duration_ms: int = Field(default=0, ge=0, description="执行耗时（毫秒）")


class HighRiskCheckResult(BaseModel):
    """[AC-IDMP-05, AC-IDMP-20] 高风险检测工具输出。"""
    matched: bool = Field(default=False, description="是否命中高风险场景")
    risk_scenario: HighRiskScenario | None = Field(
        default=None,
        description="风险场景: refund|complaint_escalation|privacy_sensitive_promise|transfer|none"
    )
    confidence: float = Field(default=0.0, ge=0.0, le=1.0, description="置信度 0~1")
    recommended_mode: ExecutionMode | None = Field(
        default=None,
        description="推荐执行模式: micro_flow|transfer|agent"
    )
    rule_id: str | None = Field(default=None, description="匹配的规则ID")
    reason: str | None = Field(default=None, description="匹配原因说明")
    fallback_reason_code: str | None = Field(default=None, description="降级原因码（工具失败时）")
    duration_ms: int = Field(default=0, ge=0, description="执行耗时（毫秒）")
    matched_text: str | None = Field(default=None, description="匹配到的文本片段")
    matched_pattern: str | None = Field(default=None, description="匹配到的模式（关键词或正则）")


class SlotSource(str, Enum):
    """[AC-IDMP-13] 槽位来源类型。"""
    USER_CONFIRMED = "user_confirmed"
    RULE_EXTRACTED = "rule_extracted"
    LLM_INFERRED = "llm_inferred"
    DEFAULT = "default"


class MemorySlot(BaseModel):
    """[AC-IDMP-13] 单个槽位信息。"""
    key: str = Field(..., description="槽位键名")
    value: Any = Field(..., description="槽位值")
    source: SlotSource = Field(default=SlotSource.DEFAULT, description="槽位来源")
    confidence: float = Field(default=1.0, ge=0.0, le=1.0, description="置信度")
    updated_at: str | None = Field(default=None, description="最后更新时间")


class MemoryRecallResult(BaseModel):
    """[AC-IDMP-13] 记忆召回工具输出。"""
    profile: dict[str, Any] = Field(default_factory=dict, description="用户基础属性")
    facts: list[str] = Field(default_factory=list, description="事实型记忆列表")
    preferences: dict[str, Any] = Field(default_factory=dict, description="用户偏好")
    last_summary: str | None = Field(default=None, description="最近会话摘要")
    slots: dict[str, MemorySlot] = Field(default_factory=dict, description="结构化槽位")
    missing_slots: list[str] = Field(default_factory=list, description="缺失的必填槽位")
    fallback_reason_code: str | None = Field(default=None, description="降级原因码")
    duration_ms: int = Field(default=0, ge=0, description="执行耗时（毫秒）")

    def get_context_for_prompt(self) -> str:
        """生成用于注入 Prompt 的上下文字符串。"""
        parts = []

        if self.profile:
            profile_parts = []
            for key, value in self.profile.items():
                if value:
                    profile_parts.append(f"{key}: {value}")
            if profile_parts:
                parts.append("【用户属性】" + "、".join(profile_parts))

        if self.facts:
            parts.append("【已知事实】" + "；".join(self.facts[:5]))

        if self.preferences:
            pref_parts = []
            for key, value in self.preferences.items():
                if value:
                    pref_parts.append(f"{key}: {value}")
            if pref_parts:
                parts.append("【用户偏好】" + "、".join(pref_parts))

        if self.last_summary:
            parts.append(f"【上次会话摘要】{self.last_summary}")

        if self.slots:
            slot_parts = []
            for key, slot in self.slots.items():
                slot_parts.append(f"{key}={slot.value}")
            if slot_parts:
                parts.append("【已知槽位】" + ", ".join(slot_parts))

        return "\n".join(parts) if parts else ""
