#!/usr/bin/env python3
"""
InsightFlow Backend - Phase 6 (API Platform)
API 开放平台：API Key 管理、Swagger 文档、限流
Knowledge Growth: Multi-file fusion + Entity Alignment + Document Import
ASR: 阿里云听悟 + OSS
"""

import io
import json
import logging
import os
import re
import sys
import time
import uuid
from datetime import datetime, timedelta
from typing import Any, Optional

import httpx
from fastapi import (
    Body,
    Depends,
    FastAPI,
    File,
    Form,
    Header,
    HTTPException,
    Query,
    Request,
    UploadFile,
)
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, PlainTextResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field

# Configure logger
logger = logging.getLogger(__name__)

# Add backend directory to path for imports
backend_dir = os.path.dirname(os.path.abspath(__file__))
if backend_dir not in sys.path:
    sys.path.insert(0, backend_dir)

# Import clients
try:
    from oss_uploader import get_oss_uploader

    OSS_AVAILABLE = True
except ImportError:
    OSS_AVAILABLE = False

try:
    from tingwu_client import TingwuClient

    TINGWU_AVAILABLE = True
except ImportError:
    TINGWU_AVAILABLE = False

try:
    from db_manager import (
        AttributeTemplate,
        Entity,
        EntityAttribute,
        EntityMention,
        get_db_manager,
    )

    DB_AVAILABLE = True
except ImportError as e:
    print(f"DB import error: {e}")
    DB_AVAILABLE = False

try:
    from document_processor import DocumentProcessor

    DOC_PROCESSOR_AVAILABLE = True
except ImportError:
    DOC_PROCESSOR_AVAILABLE = False

try:
    from entity_aligner import EntityAligner

    ALIGNER_AVAILABLE = True
except ImportError:
    ALIGNER_AVAILABLE = False

try:
    from llm_client import ChatMessage, get_llm_client

    LLM_CLIENT_AVAILABLE = True
except ImportError:
    LLM_CLIENT_AVAILABLE = False

try:
    from knowledge_reasoner import get_knowledge_reasoner

    REASONER_AVAILABLE = True
except ImportError:
    REASONER_AVAILABLE = False

try:
    from export_manager import ExportEntity, ExportRelation, ExportTranscript, get_export_manager

    EXPORT_AVAILABLE = True
except ImportError:
    EXPORT_AVAILABLE = False

try:
    from neo4j_manager import NEO4J_AVAILABLE, get_neo4j_manager, sync_project_to_neo4j
except ImportError:
    NEO4J_AVAILABLE = False

# Phase 6: API Key Manager
try:
    from api_key_manager import get_api_key_manager

    API_KEY_AVAILABLE = True
except ImportError as e:
    print(f"API Key Manager import error: {e}")
    API_KEY_AVAILABLE = False

# Phase 6: Rate Limiter
try:
    from rate_limiter import RateLimitConfig, get_rate_limiter

    RATE_LIMITER_AVAILABLE = True
except ImportError as e:
    print(f"Rate Limiter import error: {e}")
    RATE_LIMITER_AVAILABLE = False

# Phase 7: Workflow Manager
try:
    from workflow_manager import WebhookConfig, Workflow

    WORKFLOW_AVAILABLE = True
except ImportError as e:
    print(f"Workflow Manager import error: {e}")
    WORKFLOW_AVAILABLE = False

# Phase 7: Multimodal Support
try:
    from multimodal_processor import get_multimodal_processor

    MULTIMODAL_AVAILABLE = True
except ImportError as e:
    print(f"Multimodal Processor import error: {e}")
    MULTIMODAL_AVAILABLE = False

try:
    from image_processor import get_image_processor

    IMAGE_PROCESSOR_AVAILABLE = True
except ImportError as e:
    print(f"Image Processor import error: {e}")
    IMAGE_PROCESSOR_AVAILABLE = False

try:
    from multimodal_entity_linker import EntityLink, get_multimodal_entity_linker

    MULTIMODAL_LINKER_AVAILABLE = True
except ImportError as e:
    print(f"Multimodal Entity Linker import error: {e}")
    MULTIMODAL_LINKER_AVAILABLE = False

# Phase 7 Task 7: Plugin Manager
try:
    from plugin_manager import (
        BotHandler,
        Plugin,
        PluginStatus,
        PluginType,
        WebhookIntegration,
        get_plugin_manager,
    )

    PLUGIN_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Plugin Manager import error: {e}")
    PLUGIN_MANAGER_AVAILABLE = False

# Phase 7 Task 3: Security Manager
try:
    from security_manager import MaskingRuleType, get_security_manager

    SECURITY_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Security Manager import error: {e}")
    SECURITY_MANAGER_AVAILABLE = False

# Phase 7 Task 4: Collaboration Manager
try:
    from collaboration_manager import get_collaboration_manager

    COLLABORATION_AVAILABLE = True
except ImportError as e:
    print(f"Collaboration Manager import error: {e}")
    COLLABORATION_AVAILABLE = False

# Phase 7 Task 5: Report Generator
try:
    REPORT_GENERATOR_AVAILABLE = True
except ImportError as e:
    print(f"Report Generator import error: {e}")
    REPORT_GENERATOR_AVAILABLE = False

# Phase 7 Task 6: Search Manager
try:
    from search_manager import SearchOperator, get_search_manager

    SEARCH_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Search Manager import error: {e}")
    SEARCH_MANAGER_AVAILABLE = False

# Phase 7 Task 8: Performance Manager
try:
    from performance_manager import get_performance_manager

    PERFORMANCE_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Performance Manager import error: {e}")
    PERFORMANCE_MANAGER_AVAILABLE = False

# Phase 8: Tenant Manager (Multi-Tenant SaaS)
try:
    from tenant_manager import TenantRole, TenantStatus, TenantTier, get_tenant_manager

    TENANT_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Tenant Manager import error: {e}")
    TENANT_MANAGER_AVAILABLE = False

# Phase 8: Subscription Manager
try:
    from subscription_manager import get_subscription_manager

    SUBSCRIPTION_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Subscription Manager import error: {e}")
    SUBSCRIPTION_MANAGER_AVAILABLE = False

# Phase 8: Enterprise Manager
try:
    from enterprise_manager import get_enterprise_manager

    ENTERPRISE_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Enterprise Manager import error: {e}")
    ENTERPRISE_MANAGER_AVAILABLE = False

# Phase 8: Localization Manager
try:
    from localization_manager import get_localization_manager

    LOCALIZATION_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Localization Manager import error: {e}")
    LOCALIZATION_MANAGER_AVAILABLE = False

# Phase 8 Task 4: AI Manager
try:
    from ai_manager import (
        ModelStatus,
        ModelType,
        MultimodalProvider,
        PredictionType,
        get_ai_manager,
    )

    AI_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"AI Manager import error: {e}")
    AI_MANAGER_AVAILABLE = False

# Phase 8 Task 5: Growth Manager
try:
    from growth_manager import (
        EmailTemplateType,
        EventType,
        ExperimentStatus,
        GrowthManager,
        TrafficAllocationType,
        WorkflowTriggerType,
    )

    GROWTH_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Growth Manager import error: {e}")
    GROWTH_MANAGER_AVAILABLE = False

# Phase 8 Task 8: Operations & Monitoring Manager
try:
    from ops_manager import (
        AlertChannelType,
        AlertRuleType,
        AlertSeverity,
        AlertStatus,
        ResourceType,
        get_ops_manager,
    )

    OPS_MANAGER_AVAILABLE = True
except ImportError as e:
    print(f"Ops Manager import error: {e}")
    OPS_MANAGER_AVAILABLE = False

# FastAPI app with enhanced metadata for Swagger
app = FastAPI(
    title="InsightFlow API",
    description="""
    InsightFlow 知识管理平台 API

    ## 功能

    * **项目管理** - 创建、读取、更新、删除项目
    * **实体管理** - 实体提取、对齐、属性管理
    * **关系管理** - 实体关系创建、查询、分析
    * **转录管理** - 音频转录、文档导入
    * **知识推理** - 因果推理、对比分析、时序分析
    * **图分析** - Neo4j 图数据库集成、路径查询
    * **导出功能** - 多种格式导出（PDF、Excel、CSV、JSON）
    * **工作流** - 自动化任务、Webhook 通知

    ## 认证

    大部分 API 需要 API Key 认证。在请求头中添加：
    ```
    X-API-Key: your_api_key_here
    ```
    """,
    version="0.7.0",
    contact={
        "name": "InsightFlow Team",
        "url": "https://github.com/insightflow/insightflow",
    },
    license_info={
        "name": "MIT",
        "url": "https://opensource.org/licenses/MIT",
    },
    openapi_tags=[
        {"name": "Projects", "description": "项目管理"},
        {"name": "Entities", "description": "实体管理"},
        {"name": "Relations", "description": "关系管理"},
        {"name": "Transcripts", "description": "转录管理"},
        {"name": "Analysis", "description": "分析和推理"},
        {"name": "Graph", "description": "图分析和 Neo4j"},
        {"name": "Export", "description": "数据导出"},
        {"name": "API Keys", "description": "API 密钥管理"},
        {"name": "Workflows", "description": "工作流自动化"},
        {"name": "Webhooks", "description": "Webhook 配置"},
        {"name": "Multimodal", "description": "多模态支持（视频、图片）"},
        {"name": "Plugins", "description": "插件管理"},
        {"name": "Chrome Extension", "description": "Chrome 扩展集成"},
        {"name": "Bot", "description": "飞书/钉钉机器人"},
        {"name": "Integrations", "description": "Zapier/Make 集成"},
        {"name": "WebDAV", "description": "WebDAV 同步"},
        {"name": "Security", "description": "数据安全与合规（加密、脱敏、审计）"},
        {"name": "Tenants", "description": "多租户 SaaS 管理（租户、域名、品牌、成员）"},
        {"name": "Subscriptions", "description": "订阅与计费管理（计划、订阅、支付、发票、退款）"},
        {
            "name": "Enterprise",
            "description": "企业级功能（SSO/SAML、SCIM、审计日志导出、数据保留策略）",
        },
        {
            "name": "Localization",
            "description": "全球化与本地化（多语言、数据中心、支付方式、时区日历）",
        },
        {
            "name": "AI Enhancement",
            "description": "AI 能力增强（自定义模型、多模态分析、智能摘要、预测分析）",
        },
        {
            "name": "Growth & Analytics",
            "description": "运营与增长工具（用户行为分析、A/B 测试、邮件营销、推荐系统）",
        },
        {
            "name": "Operations & Monitoring",
            "description": "运维与监控（实时告警、容量规划、自动扩缩容、灾备故障转移、成本优化）",
        },
        {"name": "System", "description": "系统信息"},
    ],
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# ==================== Phase 6: API Key Authentication & Rate Limiting ====================

# 公开访问的路径（不需要 API Key）
PUBLIC_PATHS = {
    "/",
    "/docs",
    "/openapi.json",
    "/redoc",
    "/api/v1/health",
    "/api/v1/status",
    "/api/v1/api-keys",  # POST 创建 API Key 不需要认证
}

# 管理路径（需要 master key）
ADMIN_PATHS = {
    "/api/v1/admin/",
}

# Master Key（用于管理所有 API Keys）
MASTER_KEY = os.getenv("INSIGHTFLOW_MASTER_KEY", "")


async def verify_api_key(request: Request, x_api_key: str | None = Header(None, alias="X-API-Key")):
    """
    验证 API Key 的依赖函数

    - 公开路径不需要认证
    - 管理路径需要 master key
    - 其他路径需要有效的 API Key
    """
    path = request.url.path
    method = request.method

    # 公开路径直接放行
    if any(path.startswith(p) for p in PUBLIC_PATHS):
        return None

    # 创建 API Key 的端点不需要认证（但需要 master key 或其他验证）
    if path == "/api/v1/api-keys" and method == "POST":
        return None

    # 检查是否是管理路径
    if any(path.startswith(p) for p in ADMIN_PATHS):
        if not x_api_key or x_api_key != MASTER_KEY:
            raise HTTPException(
                status_code=403,
                detail="Admin access required. Provide valid master key in X-API-Key header.",
            )
        return {"type": "admin", "key": x_api_key}

    # 其他路径需要有效的 API Key
    if not API_KEY_AVAILABLE:
        # API Key 模块不可用，允许访问（开发模式）
        return None

    if not x_api_key:
        raise HTTPException(
            status_code=401,
            detail="API Key required. Provide your key in X-API-Key header.",
            headers={"WWW-Authenticate": "ApiKey"},
        )

    # 验证 API Key
    key_manager = get_api_key_manager()
    api_key = key_manager.validate_key(x_api_key)

    if not api_key:
        raise HTTPException(status_code=401, detail="Invalid or expired API Key")

    # 更新最后使用时间
    key_manager.update_last_used(api_key.id)

    # 将 API Key 信息存储在请求状态中，供后续使用
    request.state.api_key = api_key

    return {"type": "api_key", "key_id": api_key.id, "permissions": api_key.permissions}


async def rate_limit_middleware(request: Request, call_next):
    """
    限流中间件
    """
    if not RATE_LIMITER_AVAILABLE or not API_KEY_AVAILABLE:
        response = await call_next(request)
        return response

    path = request.url.path

    # 公开路径不限流
    if any(path.startswith(p) for p in PUBLIC_PATHS):
        response = await call_next(request)
        return response

    # 获取限流键
    limiter = get_rate_limiter()

    # 检查是否有 API Key
    x_api_key = request.headers.get("X-API-Key")

    if x_api_key and x_api_key == MASTER_KEY:
        # Master key 有更高的限流
        config = RateLimitConfig(requests_per_minute=1000)
        limit_key = f"master:{x_api_key[:16]}"
    elif hasattr(request.state, "api_key") and request.state.api_key:
        # 使用 API Key 的限流配置
        api_key = request.state.api_key
        config = RateLimitConfig(requests_per_minute=api_key.rate_limit)
        limit_key = f"api_key:{api_key.id}"
    else:
        # IP 限流（未认证用户）
        client_ip = request.client.host if request.client else "unknown"
        config = RateLimitConfig(requests_per_minute=10)
        limit_key = f"ip:{client_ip}"

    # 检查限流
    info = await limiter.is_allowed(limit_key, config)

    if not info.allowed:
        return JSONResponse(
            status_code=429,
            content={
                "error": "Rate limit exceeded",
                "retry_after": info.retry_after,
                "limit": config.requests_per_minute,
                "window": "minute",
            },
            headers={
                "X-RateLimit-Limit": str(config.requests_per_minute),
                "X-RateLimit-Remaining": "0",
                "X-RateLimit-Reset": str(info.reset_time),
                "Retry-After": str(info.retry_after),
            },
        )

    # 继续处理请求
    start_time = time.time()
    response = await call_next(request)

    # 添加限流头
    response.headers["X-RateLimit-Limit"] = str(config.requests_per_minute)
    response.headers["X-RateLimit-Remaining"] = str(info.remaining)
    response.headers["X-RateLimit-Reset"] = str(info.reset_time)

    # 记录 API 调用日志
    try:
        if hasattr(request.state, "api_key") and request.state.api_key:
            api_key = request.state.api_key
            response_time = int((time.time() - start_time) * 1000)
            key_manager = get_api_key_manager()
            key_manager.log_api_call(
                api_key_id=api_key.id,
                endpoint=path,
                method=request.method,
                status_code=response.status_code,
                response_time_ms=response_time,
                ip_address=request.client.host if request.client else "",
                user_agent=request.headers.get("User-Agent", ""),
            )
    except Exception as e:
        # 日志记录失败不应影响主流程
        print(f"Failed to log API call: {e}")

    return response


# 添加限流中间件
app.middleware("http")(rate_limit_middleware)

# ==================== Phase 6: Pydantic Models for API ====================

# API Key 相关模型


class ApiKeyCreate(BaseModel):
    name: str = Field(..., description="API Key 名称/描述")
    permissions: list[str] = Field(default=["read"], description="权限列表: read, write, delete")
    rate_limit: int = Field(default=60, description="每分钟请求限制")
    expires_days: int | None = Field(default=None, description="过期天数（可选）")


class ApiKeyResponse(BaseModel):
    id: str
    key_preview: str
    name: str
    permissions: list[str]
    rate_limit: int
    status: str
    created_at: str
    expires_at: str | None
    last_used_at: str | None
    total_calls: int


class ApiKeyCreateResponse(BaseModel):
    api_key: str = Field(..., description="API Key（仅显示一次，请妥善保存）")
    info: ApiKeyResponse


class ApiKeyListResponse(BaseModel):
    keys: list[ApiKeyResponse]
    total: int


class ApiKeyUpdate(BaseModel):
    name: str | None = None
    permissions: list[str] | None = None
    rate_limit: int | None = None


class ApiCallStats(BaseModel):
    total_calls: int
    success_calls: int
    error_calls: int
    avg_response_time_ms: float
    max_response_time_ms: int
    min_response_time_ms: int


class ApiStatsResponse(BaseModel):
    summary: ApiCallStats
    endpoints: list[dict]
    daily: list[dict]


class ApiCallLog(BaseModel):
    id: int
    endpoint: str
    method: str
    status_code: int
    response_time_ms: int
    ip_address: str
    user_agent: str
    error_message: str
    created_at: str


class ApiLogsResponse(BaseModel):
    logs: list[ApiCallLog]
    total: int


class RateLimitStatus(BaseModel):
    limit: int
    remaining: int
    reset_time: int
    window: str


# 原有模型（保留）
class EntityModel(BaseModel):
    id: str
    name: str
    type: str
    definition: str | None = ""
    aliases: list[str] = []


class TranscriptSegment(BaseModel):
    start: float
    end: float
    text: str
    speaker: str | None = "Speaker A"


class AnalysisResult(BaseModel):
    transcript_id: str
    project_id: str
    segments: list[TranscriptSegment]
    entities: list[EntityModel]
    full_text: str
    created_at: str


class ProjectCreate(BaseModel):
    name: str
    description: str = ""


class EntityUpdate(BaseModel):
    name: str | None = None
    type: str | None = None
    definition: str | None = None
    aliases: list[str] | None = None


class RelationCreate(BaseModel):
    source_entity_id: str
    target_entity_id: str
    relation_type: str
    evidence: str | None = ""


class TranscriptUpdate(BaseModel):
    full_text: str


class AgentQuery(BaseModel):
    query: str
    stream: bool = False


class AgentCommand(BaseModel):
    command: str


class EntityMergeRequest(BaseModel):
    source_entity_id: str
    target_entity_id: str


class GlossaryTermCreate(BaseModel):
    term: str
    pronunciation: str | None = ""


# ==================== Phase 7: Workflow Pydantic Models ====================


class WorkflowCreate(BaseModel):
    name: str = Field(..., description="工作流名称")
    description: str = Field(default="", description="工作流描述")
    workflow_type: str = Field(
        ...,
        description="工作流类型: auto_analyze, auto_align, auto_relation, scheduled_report, custom",
    )
    project_id: str = Field(..., description="所属项目ID")
    schedule: str | None = Field(default=None, description="调度表达式(cron或分钟数)")
    schedule_type: str = Field(default="manual", description="调度类型: manual, cron, interval")
    config: dict = Field(default_factory=dict, description="工作流配置")
    webhook_ids: list[str] = Field(default_factory=list, description="关联的Webhook ID列表")


class WorkflowUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    status: str | None = None  # active, paused, error, completed
    schedule: str | None = None
    schedule_type: str | None = None
    is_active: bool | None = None
    config: dict | None = None
    webhook_ids: list[str] | None = None


class WorkflowResponse(BaseModel):
    id: str
    name: str
    description: str
    workflow_type: str
    project_id: str
    status: str
    schedule: str | None
    schedule_type: str
    config: dict
    webhook_ids: list[str]
    is_active: bool
    created_at: str
    updated_at: str
    last_run_at: str | None
    next_run_at: str | None
    run_count: int
    success_count: int
    fail_count: int


class WorkflowListResponse(BaseModel):
    workflows: list[WorkflowResponse]
    total: int


class WorkflowTaskCreate(BaseModel):
    name: str = Field(..., description="任务名称")
    task_type: str = Field(
        ..., description="任务类型: analyze, align, discover_relations, notify, custom"
    )
    config: dict = Field(default_factory=dict, description="任务配置")
    order: int = Field(default=0, description="执行顺序")
    depends_on: list[str] = Field(default_factory=list, description="依赖的任务ID列表")
    timeout_seconds: int = Field(default=300, description="超时时间(秒)")
    retry_count: int = Field(default=3, description="重试次数")
    retry_delay: int = Field(default=5, description="重试延迟(秒)")


class WorkflowTaskUpdate(BaseModel):
    name: str | None = None
    task_type: str | None = None
    config: dict | None = None
    order: int | None = None
    depends_on: list[str] | None = None
    timeout_seconds: int | None = None
    retry_count: int | None = None
    retry_delay: int | None = None


class WorkflowTaskResponse(BaseModel):
    id: str
    workflow_id: str
    name: str
    task_type: str
    config: dict
    order: int
    depends_on: list[str]
    timeout_seconds: int
    retry_count: int
    retry_delay: int
    created_at: str
    updated_at: str


class WebhookCreate(BaseModel):
    name: str = Field(..., description="Webhook名称")
    webhook_type: str = Field(..., description="Webhook类型: feishu, dingtalk, slack, custom")
    url: str = Field(..., description="Webhook URL")
    secret: str = Field(default="", description="签名密钥")
    headers: dict = Field(default_factory=dict, description="自定义请求头")
    template: str = Field(default="", description="消息模板")


class WebhookUpdate(BaseModel):
    name: str | None = None
    webhook_type: str | None = None
    url: str | None = None
    secret: str | None = None
    headers: dict | None = None
    template: str | None = None
    is_active: bool | None = None


class WebhookResponse(BaseModel):
    id: str
    name: str
    webhook_type: str
    url: str
    headers: dict
    template: str
    is_active: bool
    created_at: str
    updated_at: str
    last_used_at: str | None
    success_count: int
    fail_count: int


class WebhookListResponse(BaseModel):
    webhooks: list[WebhookResponse]
    total: int


class WorkflowLogResponse(BaseModel):
    id: str
    workflow_id: str
    task_id: str | None
    status: str
    start_time: str | None
    end_time: str | None
    duration_ms: int
    input_data: dict
    output_data: dict
    error_message: str
    created_at: str


class WorkflowLogListResponse(BaseModel):
    logs: list[WorkflowLogResponse]
    total: int


class WorkflowTriggerRequest(BaseModel):
    input_data: dict = Field(default_factory=dict, description="工作流输入数据")


class WorkflowTriggerResponse(BaseModel):
    success: bool
    workflow_id: str
    log_id: str
    results: dict
    duration_ms: int


class WorkflowStatsResponse(BaseModel):
    total: int
    success: int
    failed: int
    success_rate: float
    avg_duration_ms: float
    daily: list[dict]


# API Keys
KIMI_API_KEY = os.getenv("KIMI_API_KEY", "")
KIMI_BASE_URL = os.getenv("KIMI_BASE_URL", "https://api.kimi.com/coding")

# Phase 3: Entity Aligner singleton
_aligner = None


def get_aligner():
    global _aligner
    if _aligner is None and ALIGNER_AVAILABLE:
        _aligner = EntityAligner()
    return _aligner


# Phase 3: Document Processor singleton
_doc_processor = None


def get_doc_processor():
    global _doc_processor
    if _doc_processor is None and DOC_PROCESSOR_AVAILABLE:
        _doc_processor = DocumentProcessor()
    return _doc_processor


# Phase 7 Task 4: Collaboration Manager singleton
_collaboration_manager = None


def get_collab_manager():
    global _collaboration_manager
    if _collaboration_manager is None and COLLABORATION_AVAILABLE:
        db = get_db_manager() if DB_AVAILABLE else None
        _collaboration_manager = get_collaboration_manager(db)
    return _collaboration_manager


# Phase 2: Entity Edit API


@app.put("/api/v1/entities/{entity_id}", tags=["Entities"])
async def update_entity(entity_id: str, update: EntityUpdate, _=Depends(verify_api_key)):
    """更新实体信息（名称、类型、定义、别名）"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    # 更新字段
    update_data = {k: v for k, v in update.dict().items() if v is not None}
    updated = db.update_entity(entity_id, **update_data)

    return {
        "id": updated.id,
        "name": updated.name,
        "type": updated.type,
        "definition": updated.definition,
        "aliases": updated.aliases,
    }


@app.delete("/api/v1/entities/{entity_id}", tags=["Entities"])
async def delete_entity(entity_id: str, _=Depends(verify_api_key)):
    """删除实体"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    db.delete_entity(entity_id)
    return {"success": True, "message": f"Entity {entity_id} deleted"}


@app.post("/api/v1/entities/{entity_id}/merge", tags=["Entities"])
async def merge_entities_endpoint(
    entity_id: str, merge_req: EntityMergeRequest, _=Depends(verify_api_key)
):
    """合并两个实体"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()

    # 验证两个实体都存在
    source = db.get_entity(merge_req.source_entity_id)
    target = db.get_entity(merge_req.target_entity_id)

    if not source or not target:
        raise HTTPException(status_code=404, detail="Entity not found")

    result = db.merge_entities(merge_req.target_entity_id, merge_req.source_entity_id)
    return {
        "success": True,
        "merged_entity": {
            "id": result.id,
            "name": result.name,
            "type": result.type,
            "definition": result.definition,
            "aliases": result.aliases,
        },
    }


# Phase 2: Relation Edit API


@app.post("/api/v1/projects/{project_id}/relations", tags=["Relations"])
async def create_relation_endpoint(
    project_id: str, relation: RelationCreate, _=Depends(verify_api_key)
):
    """创建新的实体关系"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()

    # 验证实体存在
    source = db.get_entity(relation.source_entity_id)
    target = db.get_entity(relation.target_entity_id)

    if not source or not target:
        raise HTTPException(status_code=404, detail="Source or target entity not found")

    relation_id = db.create_relation(
        project_id=project_id,
        source_entity_id=relation.source_entity_id,
        target_entity_id=relation.target_entity_id,
        relation_type=relation.relation_type,
        evidence=relation.evidence,
    )

    return {
        "id": relation_id,
        "source_id": relation.source_entity_id,
        "target_id": relation.target_entity_id,
        "type": relation.relation_type,
        "success": True,
    }


@app.delete("/api/v1/relations/{relation_id}", tags=["Relations"])
async def delete_relation(relation_id: str, _=Depends(verify_api_key)):
    """删除关系"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    db.delete_relation(relation_id)
    return {"success": True, "message": f"Relation {relation_id} deleted"}


@app.put("/api/v1/relations/{relation_id}", tags=["Relations"])
async def update_relation(relation_id: str, relation: RelationCreate, _=Depends(verify_api_key)):
    """更新关系"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    updated = db.update_relation(
        relation_id=relation_id, relation_type=relation.relation_type, evidence=relation.evidence
    )

    return {
        "id": relation_id,
        "type": updated["relation_type"],
        "evidence": updated["evidence"],
        "success": True,
    }


# Phase 2: Transcript Edit API


@app.get("/api/v1/transcripts/{transcript_id}", tags=["Transcripts"])
async def get_transcript(transcript_id: str, _=Depends(verify_api_key)):
    """获取转录详情"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    transcript = db.get_transcript(transcript_id)

    if not transcript:
        raise HTTPException(status_code=404, detail="Transcript not found")

    return transcript


@app.put("/api/v1/transcripts/{transcript_id}", tags=["Transcripts"])
async def update_transcript(
    transcript_id: str, update: TranscriptUpdate, _=Depends(verify_api_key)
):
    """更新转录文本（人工修正）"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    transcript = db.get_transcript(transcript_id)

    if not transcript:
        raise HTTPException(status_code=404, detail="Transcript not found")

    updated = db.update_transcript(transcript_id, update.full_text)
    return {
        "id": transcript_id,
        "full_text": updated["full_text"],
        "updated_at": updated["updated_at"],
        "success": True,
    }


# Phase 2: Manual Entity Creation


class ManualEntityCreate(BaseModel):
    name: str
    type: str = "OTHER"
    definition: str = ""
    transcript_id: str | None = None
    start_pos: int | None = None
    end_pos: int | None = None


@app.post("/api/v1/projects/{project_id}/entities", tags=["Entities"])
async def create_manual_entity(
    project_id: str, entity: ManualEntityCreate, _=Depends(verify_api_key)
):
    """手动创建实体（划词新建）"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()

    # 检查是否已存在
    existing = db.get_entity_by_name(project_id, entity.name)
    if existing:
        return {"id": existing.id, "name": existing.name, "type": existing.type, "existed": True}

    entity_id = str(uuid.uuid4())[:8]
    new_entity = db.create_entity(
        Entity(
            id=entity_id,
            project_id=project_id,
            name=entity.name,
            type=entity.type,
            definition=entity.definition,
        )
    )

    # 如果有提及位置信息，保存提及
    if entity.transcript_id and entity.start_pos is not None and entity.end_pos is not None:
        transcript = db.get_transcript(entity.transcript_id)
        if transcript:
            text = transcript["full_text"]
            mention = EntityMention(
                id=str(uuid.uuid4())[:8],
                entity_id=entity_id,
                transcript_id=entity.transcript_id,
                start_pos=entity.start_pos,
                end_pos=entity.end_pos,
                text_snippet=text[
                    max(0, entity.start_pos - 20) : min(len(text), entity.end_pos + 20)
                ],
                confidence=1.0,
            )
            db.add_mention(mention)

    return {
        "id": new_entity.id,
        "name": new_entity.name,
        "type": new_entity.type,
        "definition": new_entity.definition,
        "success": True,
    }


def transcribe_audio(audio_data: bytes, filename: str) -> dict:
    """转录音频：OSS上传 + 听悟转录"""

    # 1. 上传 OSS
    if not OSS_AVAILABLE:
        print("OSS not available, using mock")
        return mock_transcribe()

    try:
        uploader = get_oss_uploader()
        audio_url, object_name = uploader.upload_audio(audio_data, filename)
        print(f"Uploaded to OSS: {object_name}")
    except (ImportError, ModuleNotFoundError) as e:
        logger.warning(f"OSS upload failed: {e}")
        return mock_transcribe()

    # 2. 听悟转录
    if not TINGWU_AVAILABLE:
        print("Tingwu not available, using mock")
        return mock_transcribe()

    try:
        client = TingwuClient()
        result = client.transcribe(audio_url)
        print(f"Transcription complete: {len(result['segments'])} segments")
        return result
    except (ImportError, ModuleNotFoundError) as e:
        logger.warning(f"Tingwu failed: {e}")
        return mock_transcribe()


def mock_transcribe() -> dict:
    """Mock 转录结果"""
    return {
        "full_text": "我们今天讨论 Project Alpha 的进度，K8s 集群已经部署完成。",
        "segments": [
            {
                "start": 0.0,
                "end": 5.0,
                "text": "我们今天讨论 Project Alpha 的进度，K8s 集群已经部署完成。",
                "speaker": "Speaker A",
            }
        ],
    }


def extract_entities_with_llm(text: str) -> tuple[list[dict], list[dict]]:
    """使用 Kimi API 提取实体和关系

    Returns:
        (entities, relations): 实体列表和关系列表
    """
    if not KIMI_API_KEY or not text:
        return [], []

    prompt = f"""从以下会议文本中提取关键实体和它们之间的关系，以 JSON 格式返回：

文本：{text[:3000]}

要求：
1. entities: 每个实体包含 name(名称), type(类型: PROJECT/TECH/PERSON/ORG/OTHER), definition(一句话定义)
2. relations: 每个关系包含 source(源实体名), target(目标实体名), type(关系类型: belongs_to/works_with/depends_on/mentions/related)
3. 只返回 JSON 对象，格式: {{"entities": [...], "relations": [...]}}

示例：
{{
  "entities": [
    {{"name": "Project Alpha", "type": "PROJECT", "definition": "核心项目"}},
    {{"name": "K8s", "type": "TECH", "definition": "Kubernetes容器编排平台"}}
  ],
  "relations": [
    {{"source": "Project Alpha", "target": "K8s", "type": "depends_on"}}
  ]
}}
"""

    try:
        response = httpx.post(
            f"{KIMI_BASE_URL}/v1/chat/completions",
            headers={"Authorization": f"Bearer {KIMI_API_KEY}", "Content-Type": "application/json"},
            json={
                "model": "k2p5",
                "messages": [{"role": "user", "content": prompt}],
                "temperature": 0.1,
            },
            timeout=60.0,
        )
        response.raise_for_status()
        result = response.json()
        content = result["choices"][0]["message"]["content"]

        json_match = re.search(r"\{{.*?\}}", content, re.DOTALL)
        if json_match:
            data = json.loads(json_match.group())
            return data.get("entities", []), data.get("relations", [])
    except (ImportError, ModuleNotFoundError) as e:
        logger.warning(f"LLM extraction failed: {e}")

    return [], []


def align_entity(project_id: str, name: str, db, definition: str = "") -> Optional["Entity"]:
    """实体对齐 - Phase 3: 使用 embedding 对齐"""
    # 1. 首先尝试精确匹配
    existing = db.get_entity_by_name(project_id, name)
    if existing:
        return existing

    # 2. 使用 embedding 对齐（如果可用）
    aligner = get_aligner()
    if aligner:
        similar = aligner.find_similar_entity(project_id, name, definition)
        if similar:
            return similar

    # 3. 回退到简单相似度匹配
    similar = db.find_similar_entities(project_id, name)
    if similar:
        return similar[0]

    return None


# API Endpoints


@app.post("/api/v1/projects", response_model=dict, tags=["Projects"])
async def create_project(project: ProjectCreate, _=Depends(verify_api_key)):
    """创建新项目"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project_id = str(uuid.uuid4())[:8]
    p = db.create_project(project_id, project.name, project.description)
    return {"id": p.id, "name": p.name, "description": p.description}


@app.get("/api/v1/projects", tags=["Projects"])
async def list_projects(_=Depends(verify_api_key)):
    """列出所有项目"""
    if not DB_AVAILABLE:
        return []

    db = get_db_manager()
    projects = db.list_projects()
    return [{"id": p.id, "name": p.name, "description": p.description} for p in projects]


@app.post("/api/v1/projects/{project_id}/upload", response_model=AnalysisResult, tags=["Projects"])
async def upload_audio(project_id: str, file: UploadFile = File(...), _=Depends(verify_api_key)):
    """上传音频到指定项目 - Phase 3: 支持多文件融合"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    content = await file.read()

    # 转录
    print(f"Processing: {file.filename}")
    tw_result = transcribe_audio(content, file.filename)

    # 提取实体和关系
    print("Extracting entities and relations...")
    raw_entities, raw_relations = extract_entities_with_llm(tw_result["full_text"])

    # 保存转录记录
    transcript_id = str(uuid.uuid4())[:8]
    db.save_transcript(
        transcript_id=transcript_id,
        project_id=project_id,
        filename=file.filename,
        full_text=tw_result["full_text"],
    )

    # 实体对齐并保存 - Phase 3: 使用增强对齐
    aligned_entities = []
    entity_name_to_id = {}  # 用于关系映射

    for raw_ent in raw_entities:
        existing = align_entity(project_id, raw_ent["name"], db, raw_ent.get("definition", ""))

        if existing:
            ent_model = EntityModel(
                id=existing.id,
                name=existing.name,
                type=existing.type,
                definition=existing.definition,
                aliases=existing.aliases,
            )
            entity_name_to_id[raw_ent["name"]] = existing.id
        else:
            new_ent = db.create_entity(
                Entity(
                    id=str(uuid.uuid4())[:8],
                    project_id=project_id,
                    name=raw_ent["name"],
                    type=raw_ent.get("type", "OTHER"),
                    definition=raw_ent.get("definition", ""),
                )
            )
            ent_model = EntityModel(
                id=new_ent.id, name=new_ent.name, type=new_ent.type, definition=new_ent.definition
            )
            entity_name_to_id[raw_ent["name"]] = new_ent.id

        aligned_entities.append(ent_model)

        # 保存实体提及位置
        full_text = tw_result["full_text"]
        name = raw_ent["name"]
        start_pos = 0
        while True:
            pos = full_text.find(name, start_pos)
            if pos == -1:
                break
            mention = EntityMention(
                id=str(uuid.uuid4())[:8],
                entity_id=entity_name_to_id[name],
                transcript_id=transcript_id,
                start_pos=pos,
                end_pos=pos + len(name),
                text_snippet=full_text[
                    max(0, pos - 20) : min(len(full_text), pos + len(name) + 20)
                ],
                confidence=1.0,
            )
            db.add_mention(mention)
            start_pos = pos + 1

    # 保存关系
    for rel in raw_relations:
        source_id = entity_name_to_id.get(rel.get("source", ""))
        target_id = entity_name_to_id.get(rel.get("target", ""))
        if source_id and target_id:
            db.create_relation(
                project_id=project_id,
                source_entity_id=source_id,
                target_entity_id=target_id,
                relation_type=rel.get("type", "related"),
                evidence=tw_result["full_text"][:200],
                transcript_id=transcript_id,
            )

    # 构建片段
    segments = [TranscriptSegment(**seg) for seg in tw_result["segments"]]

    return AnalysisResult(
        transcript_id=transcript_id,
        project_id=project_id,
        segments=segments,
        entities=aligned_entities,
        full_text=tw_result["full_text"],
        created_at=datetime.now().isoformat(),
    )


# Phase 3: Document Upload API


@app.post("/api/v1/projects/{project_id}/upload-document")
async def upload_document(project_id: str, file: UploadFile = File(...), _=Depends(verify_api_key)):
    """上传 PDF/DOCX 文档到指定项目"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    if not DOC_PROCESSOR_AVAILABLE:
        raise HTTPException(status_code=500, detail="Document processor not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    content = await file.read()

    # 处理文档
    processor = get_doc_processor()
    try:
        result = processor.process(content, file.filename)
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Document processing failed: {str(e)}")

    # 保存文档转录记录
    transcript_id = str(uuid.uuid4())[:8]
    db.save_transcript(
        transcript_id=transcript_id,
        project_id=project_id,
        filename=file.filename,
        full_text=result["text"],
        transcript_type="document",
    )

    # 提取实体和关系
    raw_entities, raw_relations = extract_entities_with_llm(result["text"])

    # 实体对齐并保存
    aligned_entities = []
    entity_name_to_id = {}

    for raw_ent in raw_entities:
        existing = align_entity(project_id, raw_ent["name"], db, raw_ent.get("definition", ""))

        if existing:
            entity_name_to_id[raw_ent["name"]] = existing.id
            aligned_entities.append(
                EntityModel(
                    id=existing.id,
                    name=existing.name,
                    type=existing.type,
                    definition=existing.definition,
                    aliases=existing.aliases,
                )
            )
        else:
            new_ent = db.create_entity(
                Entity(
                    id=str(uuid.uuid4())[:8],
                    project_id=project_id,
                    name=raw_ent["name"],
                    type=raw_ent.get("type", "OTHER"),
                    definition=raw_ent.get("definition", ""),
                )
            )
            entity_name_to_id[raw_ent["name"]] = new_ent.id
            aligned_entities.append(
                EntityModel(
                    id=new_ent.id,
                    name=new_ent.name,
                    type=new_ent.type,
                    definition=new_ent.definition,
                )
            )

        # 保存实体提及位置
        full_text = result["text"]
        name = raw_ent["name"]
        start_pos = 0
        while True:
            pos = full_text.find(name, start_pos)
            if pos == -1:
                break
            mention = EntityMention(
                id=str(uuid.uuid4())[:8],
                entity_id=entity_name_to_id[name],
                transcript_id=transcript_id,
                start_pos=pos,
                end_pos=pos + len(name),
                text_snippet=full_text[
                    max(0, pos - 20) : min(len(full_text), pos + len(name) + 20)
                ],
                confidence=1.0,
            )
            db.add_mention(mention)
            start_pos = pos + 1

    # 保存关系
    for rel in raw_relations:
        source_id = entity_name_to_id.get(rel.get("source", ""))
        target_id = entity_name_to_id.get(rel.get("target", ""))
        if source_id and target_id:
            db.create_relation(
                project_id=project_id,
                source_entity_id=source_id,
                target_entity_id=target_id,
                relation_type=rel.get("type", "related"),
                evidence=result["text"][:200],
                transcript_id=transcript_id,
            )

    return {
        "transcript_id": transcript_id,
        "project_id": project_id,
        "filename": file.filename,
        "text_length": len(result["text"]),
        "entities": [e.dict() for e in aligned_entities],
        "created_at": datetime.now().isoformat(),
    }


# Phase 3: Knowledge Base API


@app.get("/api/v1/projects/{project_id}/knowledge-base")
async def get_knowledge_base(project_id: str, _=Depends(verify_api_key)):
    """获取项目知识库 - 包含所有实体、关系、术语表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取所有实体
    entities = db.list_project_entities(project_id)

    # 获取所有关系
    relations = db.list_project_relations(project_id)

    # 获取所有转录
    transcripts = db.list_project_transcripts(project_id)

    # 获取术语表
    glossary = db.list_glossary(project_id)

    # 构建实体统计和属性
    entity_stats = {}
    entity_attributes = {}
    for ent in entities:
        mentions = db.get_entity_mentions(ent.id)
        entity_stats[ent.id] = {
            "mention_count": len(mentions),
            "transcript_ids": list(set([m.transcript_id for m in mentions])),
        }
        # Phase 5: 获取实体属性
        attrs = db.get_entity_attributes(ent.id)
        entity_attributes[ent.id] = attrs

    # 构建实体名称映射
    entity_map = {e.id: e.name for e in entities}

    return {
        "project": {"id": project.id, "name": project.name, "description": project.description},
        "stats": {
            "entity_count": len(entities),
            "relation_count": len(relations),
            "transcript_count": len(transcripts),
            "glossary_count": len(glossary),
        },
        "entities": [
            {
                "id": e.id,
                "name": e.name,
                "type": e.type,
                "definition": e.definition,
                "aliases": e.aliases,
                "mention_count": entity_stats.get(e.id, {}).get("mention_count", 0),
                "appears_in": entity_stats.get(e.id, {}).get("transcript_ids", []),
                "attributes": entity_attributes.get(e.id, []),  # Phase 5: 包含属性
            }
            for e in entities
        ],
        "relations": [
            {
                "id": r["id"],
                "source_id": r["source_entity_id"],
                "source_name": entity_map.get(r["source_entity_id"], "Unknown"),
                "target_id": r["target_entity_id"],
                "target_name": entity_map.get(r["target_entity_id"], "Unknown"),
                "type": r["relation_type"],
                "evidence": r["evidence"],
            }
            for r in relations
        ],
        "glossary": [
            {
                "id": g["id"],
                "term": g["term"],
                "pronunciation": g["pronunciation"],
                "frequency": g["frequency"],
            }
            for g in glossary
        ],
        "transcripts": [
            {
                "id": t["id"],
                "filename": t["filename"],
                "type": t.get("type", "audio"),
                "created_at": t["created_at"],
            }
            for t in transcripts
        ],
    }


# Phase 3: Glossary API


@app.post("/api/v1/projects/{project_id}/glossary")
async def add_glossary_term(project_id: str, term: GlossaryTermCreate, _=Depends(verify_api_key)):
    """添加术语到项目术语表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    term_id = db.add_glossary_term(
        project_id=project_id, term=term.term, pronunciation=term.pronunciation
    )

    return {"id": term_id, "term": term.term, "pronunciation": term.pronunciation, "success": True}


@app.get("/api/v1/projects/{project_id}/glossary")
async def get_glossary(project_id: str, _=Depends(verify_api_key)):
    """获取项目术语表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    glossary = db.list_glossary(project_id)
    return glossary


@app.delete("/api/v1/glossary/{term_id}")
async def delete_glossary_term(term_id: str, _=Depends(verify_api_key)):
    """删除术语"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    db.delete_glossary_term(term_id)
    return {"success": True}


# Phase 3: Entity Alignment API


@app.post("/api/v1/projects/{project_id}/align-entities")
async def align_project_entities(
    project_id: str, threshold: float = 0.85, _=Depends(verify_api_key)
):
    """运行实体对齐算法，合并相似实体"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    aligner = get_aligner()
    if not aligner:
        raise HTTPException(status_code=500, detail="Entity aligner not available")

    db = get_db_manager()
    entities = db.list_project_entities(project_id)

    merged_count = 0
    merged_pairs = []

    # 使用 embedding 对齐
    for i, entity in enumerate(entities):
        # 跳过已合并的实体
        existing = db.get_entity(entity.id)
        if not existing:
            continue

        similar = aligner.find_similar_entity(
            project_id, entity.name, entity.definition, exclude_id=entity.id, threshold=threshold
        )

        if similar:
            # 合并实体
            db.merge_entities(similar.id, entity.id)
            merged_count += 1
            merged_pairs.append({"source": entity.name, "target": similar.name})

    return {"success": True, "merged_count": merged_count, "merged_pairs": merged_pairs}


@app.get("/api/v1/projects/{project_id}/entities")
async def get_project_entities(project_id: str, _=Depends(verify_api_key)):
    """获取项目的全局实体列表"""
    if not DB_AVAILABLE:
        return []

    db = get_db_manager()
    entities = db.list_project_entities(project_id)
    return [
        {
            "id": e.id,
            "name": e.name,
            "type": e.type,
            "definition": e.definition,
            "aliases": e.aliases,
        }
        for e in entities
    ]


@app.get("/api/v1/projects/{project_id}/relations")
async def get_project_relations(project_id: str, _=Depends(verify_api_key)):
    """获取项目的实体关系列表"""
    if not DB_AVAILABLE:
        return []

    db = get_db_manager()
    relations = db.list_project_relations(project_id)

    # 获取实体名称映射
    entities = db.list_project_entities(project_id)
    entity_map = {e.id: e.name for e in entities}

    return [
        {
            "id": r["id"],
            "source_id": r["source_entity_id"],
            "source_name": entity_map.get(r["source_entity_id"], "Unknown"),
            "target_id": r["target_entity_id"],
            "target_name": entity_map.get(r["target_entity_id"], "Unknown"),
            "type": r["relation_type"],
            "evidence": r["evidence"],
        }
        for r in relations
    ]


@app.get("/api/v1/projects/{project_id}/transcripts")
async def get_project_transcripts(project_id: str, _=Depends(verify_api_key)):
    """获取项目的转录列表"""
    if not DB_AVAILABLE:
        return []

    db = get_db_manager()
    transcripts = db.list_project_transcripts(project_id)
    return [
        {
            "id": t["id"],
            "filename": t["filename"],
            "type": t.get("type", "audio"),
            "created_at": t["created_at"],
            "preview": t["full_text"][:100] + "..."
            if len(t["full_text"]) > 100
            else t["full_text"],
        }
        for t in transcripts
    ]


@app.get("/api/v1/entities/{entity_id}/mentions")
async def get_entity_mentions(entity_id: str, _=Depends(verify_api_key)):
    """获取实体的所有提及位置"""
    if not DB_AVAILABLE:
        return []

    db = get_db_manager()
    mentions = db.get_entity_mentions(entity_id)
    return [
        {
            "id": m.id,
            "transcript_id": m.transcript_id,
            "start_pos": m.start_pos,
            "end_pos": m.end_pos,
            "text_snippet": m.text_snippet,
            "confidence": m.confidence,
        }
        for m in mentions
    ]


# Health check - Legacy endpoint (deprecated, use /api/v1/health)


@app.get("/health")
async def legacy_health_check():
    return {
        "status": "ok",
        "version": "0.7.0",
        "phase": "Phase 7 - Plugin & Integration",
        "oss_available": OSS_AVAILABLE,
        "tingwu_available": TINGWU_AVAILABLE,
        "db_available": DB_AVAILABLE,
        "doc_processor_available": DOC_PROCESSOR_AVAILABLE,
        "aligner_available": ALIGNER_AVAILABLE,
        "llm_client_available": LLM_CLIENT_AVAILABLE,
        "reasoner_available": REASONER_AVAILABLE,
        "multimodal_available": MULTIMODAL_AVAILABLE,
        "image_processor_available": IMAGE_PROCESSOR_AVAILABLE,
        "multimodal_linker_available": MULTIMODAL_LINKER_AVAILABLE,
        "plugin_manager_available": PLUGIN_MANAGER_AVAILABLE,
    }


# ==================== Phase 4: Agent 助手 API ====================


@app.post("/api/v1/projects/{project_id}/agent/query")
async def agent_query(project_id: str, query: AgentQuery, _=Depends(verify_api_key)):
    """Agent RAG 问答"""
    if not DB_AVAILABLE or not LLM_CLIENT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Service not available")

    db = get_db_manager()
    llm = get_llm_client()

    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目上下文
    project_context = db.get_project_summary(project_id)

    # 构建上下文
    context_parts = []
    for t in project_context.get("recent_transcripts", []):
        context_parts.append(f"【{t['filename']}】\n{t['full_text'][:1000]}")

    context = "\n\n".join(context_parts)

    if query.stream:
        # StreamingResponse 已在文件顶部导入
        async def stream_response():
            messages = [
                ChatMessage(
                    role="system", content="你是一个专业的项目分析助手，擅长从会议记录中提取洞察。"
                ),
                ChatMessage(
                    role="user",
                    content=f"""基于以下项目信息回答问题：

## 项目信息
{json.dumps(project_context, ensure_ascii=False, indent=2)}

## 相关上下文
{context[:4000]}

## 用户问题
{query.query}

请用中文回答，保持简洁专业。如果信息不足，请明确说明。""",
                ),
            ]

            async for chunk in llm.chat_stream(messages):
                yield f"data: {json.dumps({'content': chunk})}\n\n"
            yield "data: [DONE]\n\n"

        return StreamingResponse(stream_response(), media_type="text/event-stream")
    else:
        answer = await llm.rag_query(query.query, context, project_context)
        return {"answer": answer, "project_id": project_id}


@app.post("/api/v1/projects/{project_id}/agent/command")
async def agent_command(project_id: str, command: AgentCommand, _=Depends(verify_api_key)):
    """Agent 指令执行 - 解析并执行自然语言指令"""
    if not DB_AVAILABLE or not LLM_CLIENT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Service not available")

    db = get_db_manager()
    llm = get_llm_client()

    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目上下文
    project_context = db.get_project_summary(project_id)

    # 解析指令
    parsed = await llm.agent_command(command.command, project_context)

    intent = parsed.get("intent", "unknown")
    params = parsed.get("params", {})

    result = {"intent": intent, "explanation": parsed.get("explanation", "")}

    # 执行指令
    if intent == "merge_entities":
        # 合并实体
        source_names = params.get("source_names", [])
        target_name = params.get("target_name", "")

        target_entity = None
        source_entities = []

        # 查找目标实体
        for e in project_context.get("top_entities", []):
            if e["name"] == target_name or target_name in e["name"]:
                target_entity = db.get_entity_by_name(project_id, e["name"])
                break

        # 查找源实体
        for name in source_names:
            for e in project_context.get("top_entities", []):
                if e["name"] == name or name in e["name"]:
                    ent = db.get_entity_by_name(project_id, e["name"])
                    if ent and (not target_entity or ent.id != target_entity.id):
                        source_entities.append(ent)
                    break

        merged = []
        if target_entity:
            for source in source_entities:
                try:
                    db.merge_entities(target_entity.id, source.id)
                    merged.append(source.name)
                except (ImportError, ModuleNotFoundError) as e:
                    logger.warning(f"Merge failed: {e}")

        result["action"] = "merge_entities"
        result["target"] = target_entity.name if target_entity else None
        result["merged"] = merged
        result["success"] = len(merged) > 0

    elif intent == "answer_question":
        # 问答 - 调用 RAG
        answer = await llm.rag_query(params.get("question", command.command), "", project_context)
        result["action"] = "answer"
        result["answer"] = answer

    elif intent == "edit_entity":
        # 编辑实体
        entity_name = params.get("entity_name", "")
        field = params.get("field", "")
        value = params.get("value", "")

        entity = db.get_entity_by_name(project_id, entity_name)
        if entity:
            updated = db.update_entity(entity.id, **{field: value})
            result["action"] = "edit_entity"
            result["entity"] = {"id": updated.id, "name": updated.name} if updated else None
            result["success"] = updated is not None
        else:
            result["success"] = False
            result["error"] = "Entity not found"

    else:
        result["action"] = "none"
        result["message"] = (
            "无法理解的指令，请尝试：\n- 合并实体：把所有'客户端'合并到'App'\n- 提问：张总对项目的态度如何？\n- 编辑：修改'K8s'的定义为..."
        )

    return result


@app.get("/api/v1/projects/{project_id}/agent/suggest")
async def agent_suggest(project_id: str, _=Depends(verify_api_key)):
    """获取 Agent 建议 - 基于项目数据提供洞察"""
    if not DB_AVAILABLE or not LLM_CLIENT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Service not available")

    db = get_db_manager()
    llm = get_llm_client()

    project_context = db.get_project_summary(project_id)

    # 生成建议
    prompt = f"""基于以下项目数据，提供3-5条分析建议：

{json.dumps(project_context, ensure_ascii=False, indent=2)}

请提供：
1. 数据洞察发现
2. 建议的操作（如合并相似实体、补充定义等）
3. 值得关注的关键信息

返回 JSON 格式：{{"suggestions": [{{"type": "insight|action", "title": "...", "description": "..."}}]}}"""

    messages = [ChatMessage(role="user", content=prompt)]
    content = await llm.chat(messages, temperature=0.3)

    json_match = re.search(r"\{{.*?\}}", content, re.DOTALL)
    if json_match:
        try:
            data = json.loads(json_match.group())
            return data
        except (json.JSONDecodeError, ValueError):
            pass

    return {"suggestions": []}


# ==================== Phase 4: 知识溯源 API ====================


@app.get("/api/v1/relations/{relation_id}/provenance")
async def get_relation_provenance(relation_id: str, _=Depends(verify_api_key)):
    """获取关系的知识溯源信息"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    relation = db.get_relation_with_details(relation_id)

    if not relation:
        raise HTTPException(status_code=404, detail="Relation not found")

    return {
        "relation_id": relation_id,
        "source": relation.get("source_name"),
        "target": relation.get("target_name"),
        "type": relation.get("relation_type"),
        "evidence": relation.get("evidence"),
        "transcript": (
            {
                "id": relation.get("transcript_id"),
                "filename": relation.get("transcript_filename"),
            }
            if relation.get("transcript_id")
            else None
        ),
    }


@app.get("/api/v1/entities/{entity_id}/details")
async def get_entity_details(entity_id: str, _=Depends(verify_api_key)):
    """获取实体详情，包含所有提及位置"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity_with_mentions(entity_id)

    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    return entity


@app.get("/api/v1/entities/{entity_id}/evolution")
async def get_entity_evolution(entity_id: str, _=Depends(verify_api_key)):
    """分析实体的演变和态度变化"""
    if not DB_AVAILABLE or not LLM_CLIENT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Service not available")

    db = get_db_manager()
    llm = get_llm_client()

    entity = db.get_entity_with_mentions(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    # 分析演变
    analysis = await llm.analyze_entity_evolution(entity["name"], entity.get("mentions", []))

    return {
        "entity_id": entity_id,
        "entity_name": entity["name"],
        "mention_count": entity.get("mention_count", 0),
        "analysis": analysis,
        "timeline": [
            {
                "date": m.get("transcript_date"),
                "snippet": m.get("text_snippet"),
                "transcript_id": m.get("transcript_id"),
                "filename": m.get("filename"),
            }
            for m in entity.get("mentions", [])
        ],
    }


# ==================== Phase 4: 实体管理增强 API ====================


@app.get("/api/v1/projects/{project_id}/entities/search")
async def search_entities(project_id: str, q: str, _=Depends(verify_api_key)):
    """搜索实体"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entities = db.search_entities(project_id, q)
    return [
        {"id": e.id, "name": e.name, "type": e.type, "definition": e.definition} for e in entities
    ]


# ==================== Phase 5: 时间线视图 API ====================


@app.get("/api/v1/projects/{project_id}/timeline")
async def get_project_timeline(
    project_id: str,
    entity_id: str = None,
    start_date: str = None,
    end_date: str = None,
    _=Depends(verify_api_key),
):
    """获取项目时间线 - 按时间顺序的实体提及和关系事件"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    timeline = db.get_project_timeline(project_id, entity_id, start_date, end_date)

    return {"project_id": project_id, "events": timeline, "total_count": len(timeline)}


@app.get("/api/v1/projects/{project_id}/timeline/summary")
async def get_timeline_summary(project_id: str, _=Depends(verify_api_key)):
    """获取项目时间线摘要统计"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    summary = db.get_entity_timeline_summary(project_id)

    return {"project_id": project_id, "project_name": project.name, **summary}


@app.get("/api/v1/entities/{entity_id}/timeline")
async def get_entity_timeline(entity_id: str, _=Depends(verify_api_key)):
    """获取单个实体的时间线"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    timeline = db.get_project_timeline(entity.project_id, entity_id)

    return {
        "entity_id": entity_id,
        "entity_name": entity.name,
        "entity_type": entity.type,
        "events": timeline,
        "total_count": len(timeline),
    }


# ==================== Phase 5: 知识推理与问答增强 API ====================


class ReasoningQuery(BaseModel):
    query: str
    reasoning_depth: str = "medium"  # shallow/medium/deep
    stream: bool = False


@app.post("/api/v1/projects/{project_id}/reasoning/query")
async def reasoning_query(project_id: str, query: ReasoningQuery, _=Depends(verify_api_key)):
    """
    增强问答 - 基于知识推理的智能问答

    支持多种推理类型：
    - 因果推理：分析原因和影响
    - 对比推理：比较实体间的异同
    - 时序推理：分析时间线和演变
    - 关联推理：发现隐含关联
    """
    if not DB_AVAILABLE or not REASONER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Knowledge reasoner not available")

    db = get_db_manager()
    reasoner = get_knowledge_reasoner()

    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目上下文
    project_context = db.get_project_summary(project_id)

    # 获取知识图谱数据
    entities = db.list_project_entities(project_id)
    relations = db.list_project_relations(project_id)

    graph_data = {
        "entities": [
            {"id": e.id, "name": e.name, "type": e.type, "definition": e.definition}
            for e in entities
        ],
        "relations": relations,
    }

    # 执行增强问答
    result = await reasoner.enhanced_qa(
        query=query.query,
        project_context=project_context,
        graph_data=graph_data,
        reasoning_depth=query.reasoning_depth,
    )

    return {
        "answer": result.answer,
        "reasoning_type": result.reasoning_type.value,
        "confidence": result.confidence,
        "evidence": result.evidence,
        "knowledge_gaps": result.gaps,
        "project_id": project_id,
    }


@app.post("/api/v1/projects/{project_id}/reasoning/inference-path")
async def find_inference_path(
    project_id: str, start_entity: str, end_entity: str, _=Depends(verify_api_key)
):
    """
    发现两个实体之间的推理路径

    在知识图谱中搜索从 start_entity 到 end_entity 的路径
    """
    if not DB_AVAILABLE or not REASONER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Knowledge reasoner not available")

    db = get_db_manager()
    reasoner = get_knowledge_reasoner()

    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取知识图谱数据
    entities = db.list_project_entities(project_id)
    relations = db.list_project_relations(project_id)

    graph_data = {
        "entities": [{"id": e.id, "name": e.name, "type": e.type} for e in entities],
        "relations": relations,
    }

    # 查找推理路径
    paths = reasoner.find_inference_paths(start_entity, end_entity, graph_data)

    return {
        "start_entity": start_entity,
        "end_entity": end_entity,
        "paths": [
            {
                "path": path.path,
                "strength": path.strength,
                "path_description": " -> ".join([p["entity"] for p in path.path]),
            }
            for path in paths[:5]  # 最多返回5条路径
        ],
        "total_paths": len(paths),
    }


class SummaryRequest(BaseModel):
    summary_type: str = "comprehensive"  # comprehensive/executive/technical/risk


@app.post("/api/v1/projects/{project_id}/reasoning/summary")
async def project_summary(project_id: str, req: SummaryRequest, _=Depends(verify_api_key)):
    """
    项目智能总结

    根据类型生成不同侧重点的总结：
    - comprehensive: 全面总结
    - executive: 高管摘要
    - technical: 技术总结
    - risk: 风险分析
    """
    if not DB_AVAILABLE or not REASONER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Knowledge reasoner not available")

    db = get_db_manager()
    reasoner = get_knowledge_reasoner()

    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目上下文
    project_context = db.get_project_summary(project_id)

    # 获取知识图谱数据
    entities = db.list_project_entities(project_id)
    relations = db.list_project_relations(project_id)

    graph_data = {
        "entities": [{"id": e.id, "name": e.name, "type": e.type} for e in entities],
        "relations": relations,
    }

    # 生成总结
    summary = await reasoner.summarize_project(
        project_context=project_context, graph_data=graph_data, summary_type=req.summary_type
    )

    return {"project_id": project_id, "summary_type": req.summary_type, **summary**summary}


# ==================== Phase 5: 实体属性扩展 API ====================


class AttributeTemplateCreate(BaseModel):
    name: str
    type: str  # text, number, date, select, multiselect, boolean
    options: list[str] | None = None
    default_value: str | None = ""
    description: str | None = ""
    is_required: bool = False
    sort_order: int = 0


class AttributeTemplateUpdate(BaseModel):
    name: str | None = None
    type: str | None = None
    options: list[str] | None = None
    default_value: str | None = None
    description: str | None = None
    is_required: bool | None = None
    sort_order: int | None = None


class EntityAttributeSet(BaseModel):
    name: str
    type: str
    value: str | int | float | list[str] | None = None
    template_id: str | None = None
    options: list[str] | None = None
    change_reason: str | None = ""


class EntityAttributeBatchSet(BaseModel):
    attributes: list[EntityAttributeSet]
    change_reason: str | None = ""


# 属性模板管理 API
@app.post("/api/v1/projects/{project_id}/attribute-templates")
async def create_attribute_template_endpoint(
    project_id: str, template: AttributeTemplateCreate, _=Depends(verify_api_key)
):
    """创建属性模板"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    new_template = AttributeTemplate(
        id=str(uuid.uuid4())[:8],
        project_id=project_id,
        name=template.name,
        type=template.type,
        options=template.options or [],
        default_value=template.default_value or "",
        description=template.description or "",
        is_required=template.is_required,
        sort_order=template.sort_order,
    )

    db.create_attribute_template(new_template)

    return {
        "id": new_template.id,
        "name": new_template.name,
        "type": new_template.type,
        "success": True,
    }


@app.get("/api/v1/projects/{project_id}/attribute-templates")
async def list_attribute_templates_endpoint(project_id: str, _=Depends(verify_api_key)):
    """列出项目的所有属性模板"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    templates = db.list_attribute_templates(project_id)

    return [
        {
            "id": t.id,
            "name": t.name,
            "type": t.type,
            "options": t.options,
            "default_value": t.default_value,
            "description": t.description,
            "is_required": t.is_required,
            "sort_order": t.sort_order,
        }
        for t in templates
    ]


@app.get("/api/v1/attribute-templates/{template_id}")
async def get_attribute_template_endpoint(template_id: str, _=Depends(verify_api_key)):
    """获取属性模板详情"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    template = db.get_attribute_template(template_id)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {
        "id": template.id,
        "name": template.name,
        "type": template.type,
        "options": template.options,
        "default_value": template.default_value,
        "description": template.description,
        "is_required": template.is_required,
        "sort_order": template.sort_order,
    }


@app.put("/api/v1/attribute-templates/{template_id}")
async def update_attribute_template_endpoint(
    template_id: str, update: AttributeTemplateUpdate, _=Depends(verify_api_key)
):
    """更新属性模板"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    template = db.get_attribute_template(template_id)
    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    update_data = {k: v for k, v in update.dict().items() if v is not None}
    updated = db.update_attribute_template(template_id, **update_data)

    return {"id": updated.id, "name": updated.name, "type": updated.type, "success": True}


@app.delete("/api/v1/attribute-templates/{template_id}")
async def delete_attribute_template_endpoint(template_id: str, _=Depends(verify_api_key)):
    """删除属性模板"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    db.delete_attribute_template(template_id)

    return {"success": True, "message": f"Template {template_id} deleted"}


# 实体属性值管理 API
@app.post("/api/v1/entities/{entity_id}/attributes")
async def set_entity_attribute_endpoint(
    entity_id: str, attr: EntityAttributeSet, _=Depends(verify_api_key)
):
    """设置实体属性值"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    # 验证类型
    valid_types = ["text", "number", "date", "select", "multiselect"]
    if attr.type not in valid_types:
        raise HTTPException(status_code=400, detail=f"Invalid type. Must be one of: {valid_types}")

    # 处理 value
    value = attr.value
    if attr.type == "multiselect" and isinstance(value, list):
        value = json.dumps(value)
    elif value is not None:
        value = str(value)

    # 处理 options
    options = attr.options
    if options:
        options = json.dumps(options)

    # 检查是否已存在
    conn = db.get_conn()
    existing = conn.execute(
        "SELECT * FROM entity_attributes WHERE entity_id = ? AND name = ?", (entity_id, attr.name)
    ).fetchone()

    now = datetime.now().isoformat()

    if existing:
        # 记录历史
        conn.execute(
            """INSERT INTO attribute_history
               (id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                str(uuid.uuid4())[:8],
                entity_id,
                attr.name,
                existing["value"],
                value,
                "user",
                now,
                attr.change_reason or "",
            ),
        )

        # 更新
        conn.execute(
            """UPDATE entity_attributes
               SET value = ?, type = ?, options = ?, updated_at = ?
               WHERE id = ?""",
            (value, attr.type, options, now, existing["id"]),
        )
        attr_id = existing["id"]
    else:
        # 创建
        attr_id = str(uuid.uuid4())[:8]
        conn.execute(
            """INSERT INTO entity_attributes
               (id, entity_id, template_id, name, type, value, options, created_at, updated_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (attr_id, entity_id, attr.template_id, attr.name, attr.type, value, options, now, now),
        )

        # 记录历史
        conn.execute(
            """INSERT INTO attribute_history
               (id, entity_id, attribute_name, old_value, new_value, changed_by, changed_at, change_reason)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                str(uuid.uuid4())[:8],
                entity_id,
                attr.name,
                None,
                value,
                "user",
                now,
                attr.change_reason or "创建属性",
            ),
        )

    conn.commit()
    conn.close()

    return {
        "id": attr_id,
        "entity_id": entity_id,
        "name": attr.name,
        "type": attr.type,
        "value": attr.value,
        "success": True,
    }


@app.post("/api/v1/entities/{entity_id}/attributes/batch")
async def batch_set_entity_attributes_endpoint(
    entity_id: str, batch: EntityAttributeBatchSet, _=Depends(verify_api_key)
):
    """批量设置实体属性值"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    results = []
    for attr_data in batch.attributes:
        template = db.get_attribute_template(attr_data.template_id)
        if template:
            new_attr = EntityAttribute(
                id=str(uuid.uuid4())[:8],
                entity_id=entity_id,
                template_id=attr_data.template_id,
                value=attr_data.value,
            )
            db.set_entity_attribute(
                new_attr, changed_by="user", change_reason=batch.change_reason or "批量更新"
            )
            results.append(
                {
                    "template_id": attr_data.template_id,
                    "template_name": template.name,
                    "value": attr_data.value,
                }
            )

    return {
        "entity_id": entity_id,
        "updated_count": len(results),
        "attributes": results,
        "success": True,
    }


@app.get("/api/v1/entities/{entity_id}/attributes")
async def get_entity_attributes_endpoint(entity_id: str, _=Depends(verify_api_key)):
    """获取实体的所有属性值"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    entity = db.get_entity(entity_id)
    if not entity:
        raise HTTPException(status_code=404, detail="Entity not found")

    attrs = db.get_entity_attributes(entity_id)

    return [
        {
            "id": a.id,
            "template_id": a.template_id,
            "template_name": a.template_name,
            "template_type": a.template_type,
            "value": a.value,
        }
        for a in attrs
    ]


@app.delete("/api/v1/entities/{entity_id}/attributes/{template_id}")
async def delete_entity_attribute_endpoint(
    entity_id: str, template_id: str, reason: str | None = "", _=Depends(verify_api_key)
):
    """删除实体属性值"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    db.delete_entity_attribute(entity_id, template_id, changed_by="user", change_reason=reason)

    return {"success": True, "message": "Attribute deleted"}


# 属性历史 API
@app.get("/api/v1/entities/{entity_id}/attributes/history")
async def get_entity_attribute_history_endpoint(
    entity_id: str, limit: int = 50, _=Depends(verify_api_key)
):
    """获取实体的属性变更历史"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    history = db.get_attribute_history(entity_id=entity_id, limit=limit)

    return [
        {
            "id": h.id,
            "attribute_name": h.attribute_name,
            "old_value": h.old_value,
            "new_value": h.new_value,
            "changed_by": h.changed_by,
            "changed_at": h.changed_at,
            "change_reason": h.change_reason,
        }
        for h in history
    ]


@app.get("/api/v1/attribute-templates/{template_id}/history")
async def get_template_history_endpoint(
    template_id: str, limit: int = 50, _=Depends(verify_api_key)
):
    """获取属性模板的所有变更历史（跨实体）"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    history = db.get_attribute_history(template_id=template_id, limit=limit)

    return [
        {
            "id": h.id,
            "entity_id": h.entity_id,
            "template_name": h.template_name,
            "old_value": h.old_value,
            "new_value": h.new_value,
            "changed_by": h.changed_by,
            "changed_at": h.changed_at,
            "change_reason": h.change_reason,
        }
        for h in history
    ]


# 属性筛选搜索 API
@app.get("/api/v1/projects/{project_id}/entities/search-by-attributes")
async def search_entities_by_attributes_endpoint(
    project_id: str,
    attribute_filter: str | None = None,  # JSON 格式: {"职位": "经理", "部门": "技术部"}
    _=Depends(verify_api_key),
):
    """根据属性筛选搜索实体"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    filters = {}
    if attribute_filter:
        try:
            filters = json.loads(attribute_filter)
        except json.JSONDecodeError:
            raise HTTPException(status_code=400, detail="Invalid attribute_filter JSON")

    entities = db.search_entities_by_attributes(project_id, filters)

    return [
        {
            "id": e.id,
            "name": e.name,
            "type": e.type,
            "definition": e.definition,
            "attributes": e.attributes,
        }
        for e in entities
    ]


# ==================== 导出功能 API ====================


@app.get("/api/v1/projects/{project_id}/export/graph-svg")
async def export_graph_svg_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出知识图谱为 SVG"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目数据
    entities_data = db.get_project_entities(project_id)
    relations_data = db.get_project_relations(project_id)

    # 转换为导出格式
    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    relations = []
    for r in relations_data:
        relations.append(
            ExportRelation(
                id=r.id,
                source=r.source_name,
                target=r.target_name,
                relation_type=r.relation_type,
                confidence=r.confidence,
                evidence=r.evidence or "",
            )
        )

    export_mgr = get_export_manager()
    svg_content = export_mgr.export_knowledge_graph_svg(project_id, entities, relations)

    return StreamingResponse(
        io.BytesIO(svg_content.encode("utf-8")),
        media_type="image/svg+xml",
        headers={"Content-Disposition": f"attachment; filename=insightflow-graph-{project_id}.svg"},
    )


@app.get("/api/v1/projects/{project_id}/export/graph-png")
async def export_graph_png_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出知识图谱为 PNG"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目数据
    entities_data = db.get_project_entities(project_id)
    relations_data = db.get_project_relations(project_id)

    # 转换为导出格式
    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    relations = []
    for r in relations_data:
        relations.append(
            ExportRelation(
                id=r.id,
                source=r.source_name,
                target=r.target_name,
                relation_type=r.relation_type,
                confidence=r.confidence,
                evidence=r.evidence or "",
            )
        )

    export_mgr = get_export_manager()
    png_bytes = export_mgr.export_knowledge_graph_png(project_id, entities, relations)

    return StreamingResponse(
        io.BytesIO(png_bytes),
        media_type="image/png",
        headers={"Content-Disposition": f"attachment; filename=insightflow-graph-{project_id}.png"},
    )


@app.get("/api/v1/projects/{project_id}/export/entities-excel")
async def export_entities_excel_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出实体数据为 Excel"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取实体数据
    entities_data = db.get_project_entities(project_id)

    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    export_mgr = get_export_manager()
    excel_bytes = export_mgr.export_entities_excel(entities)

    return StreamingResponse(
        io.BytesIO(excel_bytes),
        media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-entities-{project_id}.xlsx"
        },
    )


@app.get("/api/v1/projects/{project_id}/export/entities-csv")
async def export_entities_csv_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出实体数据为 CSV"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取实体数据
    entities_data = db.get_project_entities(project_id)

    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    export_mgr = get_export_manager()
    csv_content = export_mgr.export_entities_csv(entities)

    return StreamingResponse(
        io.BytesIO(csv_content.encode("utf-8")),
        media_type="text/csv",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-entities-{project_id}.csv"
        },
    )


@app.get("/api/v1/projects/{project_id}/export/relations-csv")
async def export_relations_csv_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出关系数据为 CSV"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取关系数据
    relations_data = db.get_project_relations(project_id)

    relations = []
    for r in relations_data:
        relations.append(
            ExportRelation(
                id=r.id,
                source=r.source_name,
                target=r.target_name,
                relation_type=r.relation_type,
                confidence=r.confidence,
                evidence=r.evidence or "",
            )
        )

    export_mgr = get_export_manager()
    csv_content = export_mgr.export_relations_csv(relations)

    return StreamingResponse(
        io.BytesIO(csv_content.encode("utf-8")),
        media_type="text/csv",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-relations-{project_id}.csv"
        },
    )


@app.get("/api/v1/projects/{project_id}/export/report-pdf")
async def export_report_pdf_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出项目报告为 PDF"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目数据
    entities_data = db.get_project_entities(project_id)
    relations_data = db.get_project_relations(project_id)
    transcripts_data = db.get_project_transcripts(project_id)

    # 转换为导出格式
    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    relations = []
    for r in relations_data:
        relations.append(
            ExportRelation(
                id=r.id,
                source=r.source_name,
                target=r.target_name,
                relation_type=r.relation_type,
                confidence=r.confidence,
                evidence=r.evidence or "",
            )
        )

    transcripts = []
    for t in transcripts_data:
        segments = json.loads(t.segments) if t.segments else []
        transcripts.append(
            ExportTranscript(
                id=t.id,
                name=t.name,
                type=t.type,
                content=t.full_text or "",
                segments=segments,
                entity_mentions=[],
            )
        )

    # 获取项目总结
    summary = ""
    if REASONER_AVAILABLE:
        try:
            reasoner = get_knowledge_reasoner()
            summary_result = reasoner.generate_project_summary(project_id, db)
            summary = summary_result.get("summary", "")
        except Exception:
            pass

    export_mgr = get_export_manager()
    pdf_bytes = export_mgr.export_project_report_pdf(
        project_id, project.name, entities, relations, transcripts, summary
    )

    return StreamingResponse(
        io.BytesIO(pdf_bytes),
        media_type="application/pdf",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-report-{project_id}.pdf"
        },
    )


@app.get("/api/v1/projects/{project_id}/export/project-json")
async def export_project_json_endpoint(project_id: str, _=Depends(verify_api_key)):
    """导出完整项目数据为 JSON"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目数据
    entities_data = db.get_project_entities(project_id)
    relations_data = db.get_project_relations(project_id)
    transcripts_data = db.get_project_transcripts(project_id)

    # 转换为导出格式
    entities = []
    for e in entities_data:
        attrs = db.get_entity_attributes(e.id)
        entities.append(
            ExportEntity(
                id=e.id,
                name=e.name,
                type=e.type,
                definition=e.definition or "",
                aliases=json.loads(e.aliases) if e.aliases else [],
                mention_count=e.mention_count,
                attributes={a.template_name: a.value for a in attrs},
            )
        )

    relations = []
    for r in relations_data:
        relations.append(
            ExportRelation(
                id=r.id,
                source=r.source_name,
                target=r.target_name,
                relation_type=r.relation_type,
                confidence=r.confidence,
                evidence=r.evidence or "",
            )
        )

    transcripts = []
    for t in transcripts_data:
        segments = json.loads(t.segments) if t.segments else []
        transcripts.append(
            ExportTranscript(
                id=t.id,
                name=t.name,
                type=t.type,
                content=t.full_text or "",
                segments=segments,
                entity_mentions=[],
            )
        )

    export_mgr = get_export_manager()
    json_content = export_mgr.export_project_json(
        project_id, project.name, entities, relations, transcripts
    )

    return StreamingResponse(
        io.BytesIO(json_content.encode("utf-8")),
        media_type="application/json",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-project-{project_id}.json"
        },
    )


@app.get("/api/v1/transcripts/{transcript_id}/export/markdown")
async def export_transcript_markdown_endpoint(transcript_id: str, _=Depends(verify_api_key)):
    """导出转录文本为 Markdown"""
    if not DB_AVAILABLE or not EXPORT_AVAILABLE:
        raise HTTPException(status_code=500, detail="Export functionality not available")

    db = get_db_manager()
    transcript = db.get_transcript(transcript_id)
    if not transcript:
        raise HTTPException(status_code=404, detail="Transcript not found")

    # 获取实体提及
    mentions = db.get_transcript_entity_mentions(transcript_id)

    # 获取项目实体用于映射
    entities_data = db.get_project_entities(transcript.project_id)
    entities_map = {
        e.id: ExportEntity(
            id=e.id,
            name=e.name,
            type=e.type,
            definition=e.definition or "",
            aliases=json.loads(e.aliases) if e.aliases else [],
            mention_count=e.mention_count,
            attributes={},
        )
        for e in entities_data
    }

    segments = json.loads(transcript.segments) if transcript.segments else []

    export_transcript = ExportTranscript(
        id=transcript.id,
        name=transcript.name,
        type=transcript.type,
        content=transcript.full_text or "",
        segments=segments,
        entity_mentions=[
            {
                "entity_id": m.entity_id,
                "entity_name": m.entity_name,
                "position": m.position,
                "context": m.context,
            }
            for m in mentions
        ],
    )

    export_mgr = get_export_manager()
    markdown_content = export_mgr.export_transcript_markdown(export_transcript, entities_map)

    return StreamingResponse(
        io.BytesIO(markdown_content.encode("utf-8")),
        media_type="text/markdown",
        headers={
            "Content-Disposition": f"attachment; filename=insightflow-transcript-{transcript_id}.md"
        },
    )


# ==================== Neo4j Graph Database API ====================


class Neo4jSyncRequest(BaseModel):
    project_id: str


class PathQueryRequest(BaseModel):
    source_entity_id: str
    target_entity_id: str
    max_depth: int = 10


class GraphQueryRequest(BaseModel):
    entity_ids: list[str]
    depth: int = 1


@app.get("/api/v1/neo4j/status")
async def neo4j_status(_=Depends(verify_api_key)):
    """获取 Neo4j 连接状态"""
    if not NEO4J_AVAILABLE:
        return {"available": False, "connected": False, "message": "Neo4j driver not installed"}

    try:
        manager = get_neo4j_manager()
        connected = manager.is_connected()
        return {
            "available": True,
            "connected": connected,
            "uri": manager.uri if connected else None,
            "message": "Connected" if connected else "Not connected",
        }
    except Exception as e:
        return {"available": True, "connected": False, "message": str(e)}


@app.post("/api/v1/neo4j/sync")
async def neo4j_sync_project(request: Neo4jSyncRequest, _=Depends(verify_api_key)):
    """同步项目数据到 Neo4j"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    db = get_db_manager()
    project = db.get_project(request.project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取项目所有实体
    entities = db.get_project_entities(request.project_id)
    entities_data = []
    for e in entities:
        entities_data.append(
            {
                "id": e.id,
                "name": e.name,
                "type": e.type,
                "definition": e.definition,
                "aliases": json.loads(e.aliases) if e.aliases else [],
                "properties": e.attributes if hasattr(e, "attributes") else {},
            }
        )

    # 获取项目所有关系
    relations = db.get_project_relations(request.project_id)
    relations_data = []
    for r in relations:
        relations_data.append(
            {
                "id": r.id,
                "source_entity_id": r.source_entity_id,
                "target_entity_id": r.target_entity_id,
                "relation_type": r.relation_type,
                "evidence": r.evidence,
                "properties": {},
            }
        )

    # 同步到 Neo4j
    sync_project_to_neo4j(
        project_id=request.project_id,
        project_name=project.name,
        entities=entities_data,
        relations=relations_data,
    )

    return {
        "success": True,
        "project_id": request.project_id,
        "entities_synced": len(entities_data),
        "relations_synced": len(relations_data),
        "message": f"Synced {len(entities_data)} entities and {len(relations_data)} relations to Neo4j",
    }


@app.get("/api/v1/projects/{project_id}/graph/stats")
async def get_graph_stats(project_id: str, _=Depends(verify_api_key)):
    """获取项目图统计信息"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    stats = manager.get_graph_stats(project_id)
    return stats


@app.post("/api/v1/graph/shortest-path")
async def find_shortest_path(request: PathQueryRequest, _=Depends(verify_api_key)):
    """查找两个实体之间的最短路径"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    path = manager.find_shortest_path(
        request.source_entity_id, request.target_entity_id, request.max_depth
    )

    if not path:
        return {"found": False, "message": "No path found between entities"}

    return {
        "found": True,
        "path": {"nodes": path.nodes, "relationships": path.relationships, "length": path.length},
    }


@app.post("/api/v1/graph/paths")
async def find_all_paths(request: PathQueryRequest, _=Depends(verify_api_key)):
    """查找两个实体之间的所有路径"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    paths = manager.find_all_paths(
        request.source_entity_id, request.target_entity_id, request.max_depth
    )

    return {
        "count": len(paths),
        "paths": [
            {"nodes": p.nodes, "relationships": p.relationships, "length": p.length} for p in paths
        ],
    }


@app.get("/api/v1/entities/{entity_id}/neighbors")
async def get_entity_neighbors(
    entity_id: str, relation_type: str = None, limit: int = 50, _=Depends(verify_api_key)
):
    """获取实体的邻居节点"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    neighbors = manager.find_neighbors(entity_id, relation_type, limit)
    return {"entity_id": entity_id, "count": len(neighbors), "neighbors": neighbors}


@app.get("/api/v1/entities/{entity_id1}/common-neighbors/{entity_id2}")
async def get_common_neighbors(entity_id1: str, entity_id2: str, _=Depends(verify_api_key)):
    """获取两个实体的共同邻居"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    common = manager.find_common_neighbors(entity_id1, entity_id2)
    return {
        "entity_id1": entity_id1,
        "entity_id2": entity_id2,
        "count": len(common),
        "common_neighbors": common,
    }


@app.get("/api/v1/projects/{project_id}/graph/centrality")
async def get_centrality_analysis(
    project_id: str, metric: str = "degree", _=Depends(verify_api_key)
):
    """获取中心性分析结果"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    rankings = manager.find_central_entities(project_id, metric)
    return {
        "metric": metric,
        "count": len(rankings),
        "rankings": [
            {
                "entity_id": r.entity_id,
                "entity_name": r.entity_name,
                "score": r.score,
                "rank": r.rank,
            }
            for r in rankings
        ],
    }


@app.get("/api/v1/projects/{project_id}/graph/communities")
async def get_communities(project_id: str, _=Depends(verify_api_key)):
    """获取社区发现结果"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    communities = manager.detect_communities(project_id)
    return {
        "count": len(communities),
        "communities": [
            {"community_id": c.community_id, "size": c.size, "density": c.density, "nodes": c.nodes}
            for c in communities
        ],
    }


@app.post("/api/v1/graph/subgraph")
async def get_subgraph(request: GraphQueryRequest, _=Depends(verify_api_key)):
    """获取子图"""
    if not NEO4J_AVAILABLE:
        raise HTTPException(status_code=503, detail="Neo4j not available")

    manager = get_neo4j_manager()
    if not manager.is_connected():
        raise HTTPException(status_code=503, detail="Neo4j not connected")

    subgraph = manager.get_subgraph(request.entity_ids, request.depth)
    return subgraph


# ==================== Phase 6: API Key Management Endpoints ====================


@app.post("/api/v1/api-keys", response_model=ApiKeyCreateResponse, tags=["API Keys"])
async def create_api_key(request: ApiKeyCreate, _=Depends(verify_api_key)):
    """
    创建新的 API Key

    - **name**: API Key 的名称/描述
    - **permissions**: 权限列表，可选值: read, write, delete
    - **rate_limit**: 每分钟请求限制，默认 60
    - **expires_days**: 过期天数（可选，不设置则永不过期）
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()
    raw_key, api_key = key_manager.create_key(
        name=request.name,
        permissions=request.permissions,
        rate_limit=request.rate_limit,
        expires_days=request.expires_days,
    )

    return ApiKeyCreateResponse(
        api_key=raw_key,
        info=ApiKeyResponse(
            id=api_key.id,
            key_preview=api_key.key_preview,
            name=api_key.name,
            permissions=api_key.permissions,
            rate_limit=api_key.rate_limit,
            status=api_key.status,
            created_at=api_key.created_at,
            expires_at=api_key.expires_at,
            last_used_at=api_key.last_used_at,
            total_calls=api_key.total_calls,
        ),
    )


@app.get("/api/v1/api-keys", response_model=ApiKeyListResponse, tags=["API Keys"])
async def list_api_keys(
    status: str | None = None, limit: int = 100, offset: int = 0, _=Depends(verify_api_key)
):
    """
    列出所有 API Keys

    - **status**: 按状态筛选 (active, revoked, expired)
    - **limit**: 返回数量限制
    - **offset**: 分页偏移
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()
    keys = key_manager.list_keys(status=status, limit=limit, offset=offset)

    return ApiKeyListResponse(
        keys=[
            ApiKeyResponse(
                id=k.id,
                key_preview=k.key_preview,
                name=k.name,
                permissions=k.permissions,
                rate_limit=k.rate_limit,
                status=k.status,
                created_at=k.created_at,
                expires_at=k.expires_at,
                last_used_at=k.last_used_at,
                total_calls=k.total_calls,
            )
            for k in keys
        ],
        total=len(keys),
    )


@app.get("/api/v1/api-keys/{key_id}", response_model=ApiKeyResponse, tags=["API Keys"])
async def get_api_key(key_id: str, _=Depends(verify_api_key)):
    """获取单个 API Key 详情"""
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()
    key = key_manager.get_key_by_id(key_id)

    if not key:
        raise HTTPException(status_code=404, detail="API Key not found")

    return ApiKeyResponse(
        id=key.id,
        key_preview=key.key_preview,
        name=key.name,
        permissions=key.permissions,
        rate_limit=key.rate_limit,
        status=key.status,
        created_at=key.created_at,
        expires_at=key.expires_at,
        last_used_at=key.last_used_at,
        total_calls=key.total_calls,
    )


@app.patch("/api/v1/api-keys/{key_id}", response_model=ApiKeyResponse, tags=["API Keys"])
async def update_api_key(key_id: str, request: ApiKeyUpdate, _=Depends(verify_api_key)):
    """
    更新 API Key 信息

    可以更新的字段：name, permissions, rate_limit
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()

    # 构建更新数据
    updates = {}
    if request.name is not None:
        updates["name"] = request.name
    if request.permissions is not None:
        updates["permissions"] = request.permissions
    if request.rate_limit is not None:
        updates["rate_limit"] = request.rate_limit

    if not updates:
        raise HTTPException(status_code=400, detail="No fields to update")

    success = key_manager.update_key(key_id, **updates)

    if not success:
        raise HTTPException(status_code=404, detail="API Key not found")

    # 返回更新后的 key
    key = key_manager.get_key_by_id(key_id)
    return ApiKeyResponse(
        id=key.id,
        key_preview=key.key_preview,
        name=key.name,
        permissions=key.permissions,
        rate_limit=key.rate_limit,
        status=key.status,
        created_at=key.created_at,
        expires_at=key.expires_at,
        last_used_at=key.last_used_at,
        total_calls=key.total_calls,
    )


@app.delete("/api/v1/api-keys/{key_id}", tags=["API Keys"])
async def revoke_api_key(key_id: str, reason: str = "", _=Depends(verify_api_key)):
    """
    撤销 API Key

    撤销后的 Key 将无法再使用，但记录会保留用于审计
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()
    success = key_manager.revoke_key(key_id, reason=reason)

    if not success:
        raise HTTPException(status_code=404, detail="API Key not found or already revoked")

    return {"success": True, "message": f"API Key {key_id} revoked"}


@app.get("/api/v1/api-keys/{key_id}/stats", response_model=ApiStatsResponse, tags=["API Keys"])
async def get_api_key_stats(key_id: str, days: int = 30, _=Depends(verify_api_key)):
    """
    获取 API Key 的调用统计

    - **days**: 统计天数，默认 30 天
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()

    # 验证 key 存在
    key = key_manager.get_key_by_id(key_id)
    if not key:
        raise HTTPException(status_code=404, detail="API Key not found")

    stats = key_manager.get_call_stats(key_id, days=days)

    return ApiStatsResponse(
        summary=ApiCallStats(**stats["summary"]), endpoints=stats["endpoints"], daily=stats["daily"]
    )


@app.get("/api/v1/api-keys/{key_id}/logs", response_model=ApiLogsResponse, tags=["API Keys"])
async def get_api_key_logs(
    key_id: str, limit: int = 100, offset: int = 0, _=Depends(verify_api_key)
):
    """
    获取 API Key 的调用日志

    - **limit**: 返回数量限制
    - **offset**: 分页偏移
    """
    if not API_KEY_AVAILABLE:
        raise HTTPException(status_code=503, detail="API Key management not available")

    key_manager = get_api_key_manager()

    # 验证 key 存在
    key = key_manager.get_key_by_id(key_id)
    if not key:
        raise HTTPException(status_code=404, detail="API Key not found")

    logs = key_manager.get_call_logs(key_id, limit=limit, offset=offset)

    return ApiLogsResponse(
        logs=[
            ApiCallLog(
                id=log["id"],
                endpoint=log["endpoint"],
                method=log["method"],
                status_code=log["status_code"],
                response_time_ms=log["response_time_ms"],
                ip_address=log["ip_address"],
                user_agent=log["user_agent"],
                error_message=log["error_message"],
                created_at=log["created_at"],
            )
            for log in logs
        ],
        total=len(logs),
    )


@app.get("/api/v1/rate-limit/status", response_model=RateLimitStatus, tags=["API Keys"])
async def get_rate_limit_status(request: Request, _=Depends(verify_api_key)):
    """获取当前请求的限流状态"""
    if not RATE_LIMITER_AVAILABLE:
        return RateLimitStatus(
            limit=60, remaining=60, reset_time=int(time.time()) + 60, window="minute"
        )

    limiter = get_rate_limiter()

    # 获取限流键
    if hasattr(request.state, "api_key") and request.state.api_key:
        api_key = request.state.api_key
        limit_key = f"api_key:{api_key.id}"
        limit = api_key.rate_limit
    else:
        client_ip = request.client.host if request.client else "unknown"
        limit_key = f"ip:{client_ip}"
        limit = 10

    info = await limiter.get_limit_info(limit_key)

    return RateLimitStatus(
        limit=limit, remaining=info.remaining, reset_time=info.reset_time, window="minute"
    )


# ==================== Phase 6: System Endpoints ====================


@app.get("/api/v1/health", tags=["System"])
async def api_health_check():
    """健康检查端点"""
    return {"status": "healthy", "version": "0.7.0", "timestamp": datetime.now().isoformat()}


@app.get("/api/v1/status", tags=["System"])
async def system_status():
    """系统状态信息"""
    status = {
        "version": "0.7.0",
        "phase": "Phase 7 - Plugin & Integration",
        "features": {
            "database": DB_AVAILABLE,
            "oss": OSS_AVAILABLE,
            "tingwu": TINGWU_AVAILABLE,
            "llm": LLM_CLIENT_AVAILABLE,
            "neo4j": NEO4J_AVAILABLE,
            "export": EXPORT_AVAILABLE,
            "api_keys": API_KEY_AVAILABLE,
            "rate_limiting": RATE_LIMITER_AVAILABLE,
            "workflow": WORKFLOW_AVAILABLE,
            "multimodal": MULTIMODAL_AVAILABLE,
            "multimodal_linker": MULTIMODAL_LINKER_AVAILABLE,
            "plugin_manager": PLUGIN_MANAGER_AVAILABLE,
        },
        "api": {
            "documentation": "/docs",
            "openapi": "/openapi.json",
        },
        "timestamp": datetime.now().isoformat(),
    }

    return status


# ==================== Phase 7: Workflow Automation Endpoints ====================

# Workflow Manager singleton
_workflow_manager = None


def get_workflow_manager_instance():
    global _workflow_manager
    if _workflow_manager is None and WORKFLOW_AVAILABLE and DB_AVAILABLE:
        from workflow_manager import WorkflowManager

        db = get_db_manager()
        _workflow_manager = WorkflowManager(db)
        _workflow_manager.start()
    return _workflow_manager


@app.post("/api/v1/workflows", response_model=WorkflowResponse, tags=["Workflows"])
async def create_workflow_endpoint(request: WorkflowCreate, _=Depends(verify_api_key)):
    """
    创建工作流

    工作流类型:
    - **auto_analyze**: 自动分析新上传的文件
    - **auto_align**: 自动实体对齐
    - **auto_relation**: 自动关系发现
    - **scheduled_report**: 定时报告
    - **custom**: 自定义工作流

    调度类型:
    - **manual**: 手动触发
    - **cron**: Cron 表达式调度
    - **interval**: 间隔调度（分钟数）

    定时规则示例:
    - `0 9 * * *` - 每天上午9点 (cron)
    - `60` - 每60分钟执行一次 (interval)
    """
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()

    try:
        workflow = Workflow(
            id=str(uuid.uuid4())[:8],
            name=request.name,
            description=request.description,
            workflow_type=request.workflow_type,
            project_id=request.project_id,
            schedule=request.schedule,
            schedule_type=request.schedule_type,
            config=request.config,
            webhook_ids=request.webhook_ids,
        )

        created = manager.create_workflow(workflow)

        return WorkflowResponse(
            id=created.id,
            name=created.name,
            description=created.description,
            workflow_type=created.workflow_type,
            project_id=created.project_id,
            status=created.status,
            schedule=created.schedule,
            schedule_type=created.schedule_type,
            config=created.config,
            webhook_ids=created.webhook_ids,
            is_active=created.is_active,
            created_at=created.created_at,
            updated_at=created.updated_at,
            last_run_at=created.last_run_at,
            next_run_at=created.next_run_at,
            run_count=created.run_count,
            success_count=created.success_count,
            fail_count=created.fail_count,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/workflows", response_model=WorkflowListResponse, tags=["Workflows"])
async def list_workflows_endpoint(
    project_id: str | None = None,
    status: str | None = None,
    workflow_type: str | None = None,
    _=Depends(verify_api_key),
):
    """获取工作流列表"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    workflows = manager.list_workflows(project_id, status, workflow_type)

    return WorkflowListResponse(
        workflows=[
            WorkflowResponse(
                id=w.id,
                name=w.name,
                description=w.description,
                workflow_type=w.workflow_type,
                project_id=w.project_id,
                status=w.status,
                schedule=w.schedule,
                schedule_type=w.schedule_type,
                config=w.config,
                webhook_ids=w.webhook_ids,
                is_active=w.is_active,
                created_at=w.created_at,
                updated_at=w.updated_at,
                last_run_at=w.last_run_at,
                next_run_at=w.next_run_at,
                run_count=w.run_count,
                success_count=w.success_count,
                fail_count=w.fail_count,
            )
            for w in workflows
        ],
        total=len(workflows),
    )


@app.get("/api/v1/workflows/{workflow_id}", response_model=WorkflowResponse, tags=["Workflows"])
async def get_workflow_endpoint(workflow_id: str, _=Depends(verify_api_key)):
    """获取单个工作流详情"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    workflow = manager.get_workflow(workflow_id)

    if not workflow:
        raise HTTPException(status_code=404, detail="Workflow not found")

    return WorkflowResponse(
        id=workflow.id,
        name=workflow.name,
        description=workflow.description,
        workflow_type=workflow.workflow_type,
        project_id=workflow.project_id,
        status=workflow.status,
        schedule=workflow.schedule,
        schedule_type=workflow.schedule_type,
        config=workflow.config,
        webhook_ids=workflow.webhook_ids,
        is_active=workflow.is_active,
        created_at=workflow.created_at,
        updated_at=workflow.updated_at,
        last_run_at=workflow.last_run_at,
        next_run_at=workflow.next_run_at,
        run_count=workflow.run_count,
        success_count=workflow.success_count,
        fail_count=workflow.fail_count,
    )


@app.patch("/api/v1/workflows/{workflow_id}", response_model=WorkflowResponse, tags=["Workflows"])
async def update_workflow_endpoint(
    workflow_id: str, request: WorkflowUpdate, _=Depends(verify_api_key)
):
    """更新工作流"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()

    update_data = {k: v for k, v in request.dict().items() if v is not None}
    updated = manager.update_workflow(workflow_id, **update_data)

    if not updated:
        raise HTTPException(status_code=404, detail="Workflow not found")

    return WorkflowResponse(
        id=updated.id,
        name=updated.name,
        description=updated.description,
        workflow_type=updated.workflow_type,
        project_id=updated.project_id,
        status=updated.status,
        schedule=updated.schedule,
        schedule_type=updated.schedule_type,
        config=updated.config,
        webhook_ids=updated.webhook_ids,
        is_active=updated.is_active,
        created_at=updated.created_at,
        updated_at=updated.updated_at,
        last_run_at=updated.last_run_at,
        next_run_at=updated.next_run_at,
        run_count=updated.run_count,
        success_count=updated.success_count,
        fail_count=updated.fail_count,
    )


@app.delete("/api/v1/workflows/{workflow_id}", tags=["Workflows"])
async def delete_workflow_endpoint(workflow_id: str, _=Depends(verify_api_key)):
    """删除工作流"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    success = manager.delete_workflow(workflow_id)

    if not success:
        raise HTTPException(status_code=404, detail="Workflow not found")

    return {"success": True, "message": "Workflow deleted successfully"}


@app.post(
    "/api/v1/workflows/{workflow_id}/trigger",
    response_model=WorkflowTriggerResponse,
    tags=["Workflows"],
)
async def trigger_workflow_endpoint(
    workflow_id: str, request: WorkflowTriggerRequest = None, _=Depends(verify_api_key)
):
    """手动触发工作流"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()

    try:
        result = await manager.execute_workflow(
            workflow_id, input_data=request.input_data if request else {}
        )

        return WorkflowTriggerResponse(
            success=result["success"],
            workflow_id=result["workflow_id"],
            log_id=result["log_id"],
            results=result["results"],
            duration_ms=result["duration_ms"],
        )
    except ValueError as e:
        raise HTTPException(status_code=404, detail=str(e))
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.get(
    "/api/v1/workflows/{workflow_id}/logs",
    response_model=WorkflowLogListResponse,
    tags=["Workflows"],
)
async def get_workflow_logs_endpoint(
    workflow_id: str,
    status: str | None = None,
    limit: int = 100,
    offset: int = 0,
    _=Depends(verify_api_key),
):
    """获取工作流执行日志"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    logs = manager.list_logs(workflow_id=workflow_id, status=status, limit=limit, offset=offset)

    return WorkflowLogListResponse(
        logs=[
            WorkflowLogResponse(
                id=log.id,
                workflow_id=log.workflow_id,
                task_id=log.task_id,
                status=log.status,
                start_time=log.start_time,
                end_time=log.end_time,
                duration_ms=log.duration_ms,
                input_data=log.input_data,
                output_data=log.output_data,
                error_message=log.error_message,
                created_at=log.created_at,
            )
            for log in logs
        ],
        total=len(logs),
    )


@app.get(
    "/api/v1/workflows/{workflow_id}/stats",
    response_model=WorkflowStatsResponse,
    tags=["Workflows"],
)
async def get_workflow_stats_endpoint(workflow_id: str, days: int = 30, _=Depends(verify_api_key)):
    """获取工作流执行统计"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    stats = manager.get_workflow_stats(workflow_id, days)

    return WorkflowStatsResponse(**stats)


# ==================== Phase 7: Webhook Endpoints ====================


@app.post("/api/v1/webhooks", response_model=WebhookResponse, tags=["Webhooks"])
async def create_webhook_endpoint(request: WebhookCreate, _=Depends(verify_api_key)):
    """
    创建 Webhook 配置

    Webhook 类型:
    - **feishu**: 飞书机器人
    - **dingtalk**: 钉钉机器人
    - **slack**: Slack Incoming Webhook
    - **custom**: 自定义 Webhook
    """
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()

    try:
        webhook = WebhookConfig(
            id=str(uuid.uuid4())[:8],
            name=request.name,
            webhook_type=request.webhook_type,
            url=request.url,
            secret=request.secret,
            headers=request.headers,
            template=request.template,
        )

        created = manager.create_webhook(webhook)

        return WebhookResponse(
            id=created.id,
            name=created.name,
            webhook_type=created.webhook_type,
            url=created.url,
            headers=created.headers,
            template=created.template,
            is_active=created.is_active,
            created_at=created.created_at,
            updated_at=created.updated_at,
            last_used_at=created.last_used_at,
            success_count=created.success_count,
            fail_count=created.fail_count,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/webhooks", response_model=WebhookListResponse, tags=["Webhooks"])
async def list_webhooks_endpoint(_=Depends(verify_api_key)):
    """获取 Webhook 列表"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    webhooks = manager.list_webhooks()

    return WebhookListResponse(
        webhooks=[
            WebhookResponse(
                id=w.id,
                name=w.name,
                webhook_type=w.webhook_type,
                url=w.url,
                headers=w.headers,
                template=w.template,
                is_active=w.is_active,
                created_at=w.created_at,
                updated_at=w.updated_at,
                last_used_at=w.last_used_at,
                success_count=w.success_count,
                fail_count=w.fail_count,
            )
            for w in webhooks
        ],
        total=len(webhooks),
    )


@app.get("/api/v1/webhooks/{webhook_id}", response_model=WebhookResponse, tags=["Webhooks"])
async def get_webhook_endpoint(webhook_id: str, _=Depends(verify_api_key)):
    """获取单个 Webhook 详情"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    webhook = manager.get_webhook(webhook_id)

    if not webhook:
        raise HTTPException(status_code=404, detail="Webhook not found")

    return WebhookResponse(
        id=webhook.id,
        name=webhook.name,
        webhook_type=webhook.webhook_type,
        url=webhook.url,
        headers=webhook.headers,
        template=webhook.template,
        is_active=webhook.is_active,
        created_at=webhook.created_at,
        updated_at=webhook.updated_at,
        last_used_at=webhook.last_used_at,
        success_count=webhook.success_count,
        fail_count=webhook.fail_count,
    )


@app.patch("/api/v1/webhooks/{webhook_id}", response_model=WebhookResponse, tags=["Webhooks"])
async def update_webhook_endpoint(
    webhook_id: str, request: WebhookUpdate, _=Depends(verify_api_key)
):
    """更新 Webhook 配置"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()

    update_data = {k: v for k, v in request.dict().items() if v is not None}
    updated = manager.update_webhook(webhook_id, **update_data)

    if not updated:
        raise HTTPException(status_code=404, detail="Webhook not found")

    return WebhookResponse(
        id=updated.id,
        name=updated.name,
        webhook_type=updated.webhook_type,
        url=updated.url,
        headers=updated.headers,
        template=updated.template,
        is_active=updated.is_active,
        created_at=updated.created_at,
        updated_at=updated.updated_at,
        last_used_at=updated.last_used_at,
        success_count=updated.success_count,
        fail_count=updated.fail_count,
    )


@app.delete("/api/v1/webhooks/{webhook_id}", tags=["Webhooks"])
async def delete_webhook_endpoint(webhook_id: str, _=Depends(verify_api_key)):
    """删除 Webhook 配置"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    success = manager.delete_webhook(webhook_id)

    if not success:
        raise HTTPException(status_code=404, detail="Webhook not found")

    return {"success": True, "message": "Webhook deleted successfully"}


@app.post("/api/v1/webhooks/{webhook_id}/test", tags=["Webhooks"])
async def test_webhook_endpoint(webhook_id: str, _=Depends(verify_api_key)):
    """测试 Webhook 配置"""
    if not WORKFLOW_AVAILABLE:
        raise HTTPException(status_code=503, detail="Workflow automation not available")

    manager = get_workflow_manager_instance()
    webhook = manager.get_webhook(webhook_id)

    if not webhook:
        raise HTTPException(status_code=404, detail="Webhook not found")

    # 构建测试消息
    test_message = {
        "content": "🔔 这是来自 InsightFlow 的 Webhook 测试消息\n\n如果您收到这条消息，说明 Webhook 配置正确！"
    }

    if webhook.webhook_type == "slack":
        test_message = {
            "text": "🔔 这是来自 InsightFlow 的 Webhook 测试消息\n\n如果您收到这条消息，说明 Webhook 配置正确！"
        }

    success = await manager.notifier.send(webhook, test_message)
    manager.update_webhook_stats(webhook_id, success)

    if success:
        return {"success": True, "message": "Webhook test sent successfully"}
    else:
        raise HTTPException(status_code=400, detail="Webhook test failed")


# ==================== Phase 7: Multimodal Support Endpoints ====================


# Pydantic Models for Multimodal API
class VideoUploadResponse(BaseModel):
    video_id: str
    project_id: str
    filename: str
    status: str
    audio_extracted: bool
    frame_count: int
    ocr_text_preview: str
    message: str


class ImageUploadResponse(BaseModel):
    image_id: str
    project_id: str
    filename: str
    image_type: str
    ocr_text_preview: str
    description: str
    entity_count: int
    status: str


class MultimodalEntityLinkResponse(BaseModel):
    link_id: str
    source_entity_id: str
    target_entity_id: str
    source_modality: str
    target_modality: str
    link_type: str
    confidence: float
    evidence: str


class MultimodalAlignmentRequest(BaseModel):
    project_id: str
    threshold: float = 0.85


class MultimodalAlignmentResponse(BaseModel):
    project_id: str
    aligned_count: int
    links: list[MultimodalEntityLinkResponse]
    message: str


class MultimodalStatsResponse(BaseModel):
    project_id: str
    video_count: int
    image_count: int
    multimodal_entity_count: int
    cross_modal_links: int
    modality_distribution: dict[str, int]


@app.post(
    "/api/v1/projects/{project_id}/upload-video",
    response_model=VideoUploadResponse,
    tags=["Multimodal"],
)
async def upload_video_endpoint(
    project_id: str,
    file: UploadFile = File(...),
    extract_interval: int = Form(5),
    _=Depends(verify_api_key),
):
    """
    上传视频文件进行处理

    - 提取音频轨道
    - 提取关键帧（每 N 秒一帧）
    - 对关键帧进行 OCR 识别
    - 将视频、音频、OCR 结果整合

    **参数:**
    - **extract_interval**: 关键帧提取间隔（秒），默认 5 秒
    """
    if not MULTIMODAL_AVAILABLE:
        raise HTTPException(status_code=503, detail="Multimodal processing not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 读取视频文件
    video_data = await file.read()

    # 创建视频处理器
    processor = get_multimodal_processor(frame_interval=extract_interval)

    # 处理视频
    video_id = str(uuid.uuid4())[:8]
    result = processor.process_video(video_data, file.filename, project_id, video_id)

    if not result.success:
        raise HTTPException(
            status_code=500, detail=f"Video processing failed: {result.error_message}"
        )

    # 保存视频信息到数据库
    conn = db.get_conn()
    now = datetime.now().isoformat()

    # 获取视频信息
    video_info = processor.extract_video_info(
        os.path.join(processor.video_dir, f"{video_id}_{file.filename}")
    )

    conn.execute(
        """INSERT INTO videos
           (id, project_id, filename, duration, fps, resolution,
            audio_transcript_id, full_ocr_text, extracted_entities,
            extracted_relations, status, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
        (
            video_id,
            project_id,
            file.filename,
            video_info.get("duration", 0),
            video_info.get("fps", 0),
            json.dumps(
                {"width": video_info.get("width", 0), "height": video_info.get("height", 0)}
            ),
            None,
            result.full_text,
            "[]",
            "[]",
            "completed",
            now,
            now,
        ),
    )

    # 保存关键帧信息
    for frame in result.frames:
        conn.execute(
            """INSERT INTO video_frames
               (id, video_id, frame_number, timestamp, image_url, ocr_text, extracted_entities, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                frame.id,
                frame.video_id,
                frame.frame_number,
                frame.timestamp,
                frame.frame_path,
                frame.ocr_text,
                json.dumps(frame.entities_detected),
                now,
            ),
        )

    conn.commit()
    conn.close()

    # 提取实体和关系（复用现有的 LLM 提取逻辑）
    if result.full_text:
        raw_entities, raw_relations = extract_entities_with_llm(result.full_text)

        # 实体对齐并保存
        entity_name_to_id = {}
        for raw_ent in raw_entities:
            existing = align_entity(project_id, raw_ent["name"], db, raw_ent.get("definition", ""))

            if existing:
                entity_name_to_id[raw_ent["name"]] = existing.id
            else:
                new_ent = db.create_entity(
                    Entity(
                        id=str(uuid.uuid4())[:8],
                        project_id=project_id,
                        name=raw_ent["name"],
                        type=raw_ent.get("type", "OTHER"),
                        definition=raw_ent.get("definition", ""),
                    )
                )
                entity_name_to_id[raw_ent["name"]] = new_ent.id

            # 保存多模态实体提及
            conn = db.get_conn()
            conn.execute(
                """INSERT OR REPLACE INTO multimodal_mentions
                   (id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    str(uuid.uuid4())[:8],
                    project_id,
                    entity_name_to_id[raw_ent["name"]],
                    "video",
                    video_id,
                    "video_frame",
                    raw_ent.get("name", ""),
                    1.0,
                    now,
                ),
            )
            conn.commit()
            conn.close()

        # 保存关系
        for rel in raw_relations:
            source_id = entity_name_to_id.get(rel.get("source", ""))
            target_id = entity_name_to_id.get(rel.get("target", ""))
            if source_id and target_id:
                db.create_relation(
                    project_id=project_id,
                    source_entity_id=source_id,
                    target_entity_id=target_id,
                    relation_type=rel.get("type", "related"),
                    evidence=result.full_text[:200],
                )

        # 更新视频的实体和关系信息
        conn = db.get_conn()
        conn.execute(
            "UPDATE videos SET extracted_entities = ?, extracted_relations = ? WHERE id = ?",
            (json.dumps(raw_entities), json.dumps(raw_relations), video_id),
        )
        conn.commit()
        conn.close()

    return VideoUploadResponse(
        video_id=video_id,
        project_id=project_id,
        filename=file.filename,
        status="completed",
        audio_extracted=bool(result.audio_path),
        frame_count=len(result.frames),
        ocr_text_preview=result.full_text[:200] + "..."
        if len(result.full_text) > 200
        else result.full_text,
        message="Video processed successfully",
    )


@app.post(
    "/api/v1/projects/{project_id}/upload-image",
    response_model=ImageUploadResponse,
    tags=["Multimodal"],
)
async def upload_image_endpoint(
    project_id: str,
    file: UploadFile = File(...),
    detect_type: bool = Form(True),
    _=Depends(verify_api_key),
):
    """
    上传图片文件进行处理

    - 图片内容识别（白板、PPT、手写笔记）
    - 使用 OCR 识别图片中的文字
    - 提取图片中的实体和关系

    **参数:**
    - **detect_type**: 是否自动检测图片类型，默认 True
    """
    if not IMAGE_PROCESSOR_AVAILABLE:
        raise HTTPException(status_code=503, detail="Image processing not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 读取图片文件
    image_data = await file.read()

    # 创建图片处理器
    processor = get_image_processor()

    # 处理图片
    image_id = str(uuid.uuid4())[:8]
    result = processor.process_image(image_data, file.filename, image_id, detect_type)

    if not result.success:
        raise HTTPException(
            status_code=500, detail=f"Image processing failed: {result.error_message}"
        )

    # 保存图片信息到数据库
    conn = db.get_conn()
    now = datetime.now().isoformat()

    conn.execute(
        """INSERT INTO images
           (id, project_id, filename, ocr_text, description,
            extracted_entities, extracted_relations, status, created_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
        (
            image_id,
            project_id,
            file.filename,
            result.ocr_text,
            result.description,
            json.dumps(
                [
                    {"name": e.name, "type": e.type, "confidence": e.confidence}
                    for e in result.entities
                ]
            ),
            json.dumps(
                [
                    {"source": r.source, "target": r.target, "type": r.relation_type}
                    for r in result.relations
                ]
            ),
            "completed",
            now,
            now,
        ),
    )
    conn.commit()
    conn.close()

    # 保存提取的实体
    for entity in result.entities:
        existing = align_entity(project_id, entity.name, db, "")

        if not existing:
            new_ent = db.create_entity(
                Entity(
                    id=str(uuid.uuid4())[:8],
                    project_id=project_id,
                    name=entity.name,
                    type=entity.type,
                    definition="",
                )
            )
            entity_id = new_ent.id
        else:
            entity_id = existing.id

        # 保存多模态实体提及
        conn = db.get_conn()
        conn.execute(
            """INSERT OR REPLACE INTO multimodal_mentions
               (id, project_id, entity_id, modality, source_id, source_type, text_snippet, confidence, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                str(uuid.uuid4())[:8],
                project_id,
                entity_id,
                "image",
                image_id,
                result.image_type,
                entity.name,
                entity.confidence,
                now,
            ),
        )
        conn.commit()
        conn.close()

    # 保存提取的关系
    for relation in result.relations:
        source_entity = db.get_entity_by_name(project_id, relation.source)
        target_entity = db.get_entity_by_name(project_id, relation.target)

        if source_entity and target_entity:
            db.create_relation(
                project_id=project_id,
                source_entity_id=source_entity.id,
                target_entity_id=target_entity.id,
                relation_type=relation.relation_type,
                evidence=result.ocr_text[:200],
            )

    return ImageUploadResponse(
        image_id=image_id,
        project_id=project_id,
        filename=file.filename,
        image_type=result.image_type,
        ocr_text_preview=result.ocr_text[:200] + "..."
        if len(result.ocr_text) > 200
        else result.ocr_text,
        description=result.description,
        entity_count=len(result.entities),
        status="completed",
    )


@app.post("/api/v1/projects/{project_id}/upload-images-batch", tags=["Multimodal"])
async def upload_images_batch_endpoint(
    project_id: str, files: list[UploadFile] = File(...), _=Depends(verify_api_key)
):
    """
    批量上传图片文件进行处理

    支持一次上传多张图片，每张图片都会进行 OCR 和实体提取
    """
    if not IMAGE_PROCESSOR_AVAILABLE:
        raise HTTPException(status_code=503, detail="Image processing not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 读取所有图片
    images_data = []
    for file in files:
        image_data = await file.read()
        images_data.append((image_data, file.filename))

    # 批量处理
    processor = get_image_processor()
    batch_result = processor.process_batch(images_data, project_id)

    # 保存结果
    results = []
    for result in batch_result.results:
        if result.success:
            image_id = result.image_id

            # 保存到数据库
            conn = db.get_conn()
            now = datetime.now().isoformat()

            conn.execute(
                """INSERT INTO images
                   (id, project_id, filename, ocr_text, description,
                    extracted_entities, extracted_relations, status, created_at, updated_at)
                   VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
                (
                    image_id,
                    project_id,
                    "batch_image",
                    result.ocr_text,
                    result.description,
                    json.dumps([{"name": e.name, "type": e.type} for e in result.entities]),
                    json.dumps(
                        [{"source": r.source, "target": r.target} for r in result.relations]
                    ),
                    "completed",
                    now,
                    now,
                ),
            )
            conn.commit()
            conn.close()

            results.append(
                {
                    "image_id": image_id,
                    "status": "success",
                    "image_type": result.image_type,
                    "entity_count": len(result.entities),
                }
            )
        else:
            results.append(
                {"image_id": result.image_id, "status": "failed", "error": result.error_message}
            )

    return {
        "project_id": project_id,
        "total_count": batch_result.total_count,
        "success_count": batch_result.success_count,
        "failed_count": batch_result.failed_count,
        "results": results,
    }


@app.post(
    "/api/v1/projects/{project_id}/multimodal/align",
    response_model=MultimodalAlignmentResponse,
    tags=["Multimodal"],
)
async def align_multimodal_entities_endpoint(
    project_id: str, threshold: float = 0.85, _=Depends(verify_api_key)
):
    """
    跨模态实体对齐

    对齐同一实体在不同模态（音频、视频、图片、文档）中的提及

    **参数:**
    - **threshold**: 相似度阈值，默认 0.85
    """
    if not MULTIMODAL_LINKER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Multimodal entity linker not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取所有实体
    db.list_project_entities(project_id)

    # 获取多模态提及
    conn = db.get_conn()
    mentions = conn.execute(
        """SELECT * FROM multimodal_mentions WHERE project_id = ?""", (project_id,)
    ).fetchall()
    conn.close()

    # 按模态分组实体
    modality_entities = {"audio": [], "video": [], "image": [], "document": []}

    for mention in mentions:
        modality = mention["modality"]
        entity = db.get_entity(mention["entity_id"])
        if entity and entity.id not in [e.get("id") for e in modality_entities[modality]]:
            modality_entities[modality].append(
                {
                    "id": entity.id,
                    "name": entity.name,
                    "type": entity.type,
                    "definition": entity.definition,
                    "aliases": entity.aliases,
                }
            )

    # 跨模态对齐
    linker = get_multimodal_entity_linker(similarity_threshold=threshold)
    links = linker.align_cross_modal_entities(
        project_id=project_id,
        audio_entities=modality_entities["audio"],
        video_entities=modality_entities["video"],
        image_entities=modality_entities["image"],
        document_entities=modality_entities["document"],
    )

    # 保存关联到数据库
    conn = db.get_conn()
    now = datetime.now().isoformat()

    saved_links = []
    for link in links:
        conn.execute(
            """INSERT OR REPLACE INTO multimodal_entity_links
               (id, entity_id, linked_entity_id, link_type, confidence, evidence, modalities, created_at)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?)""",
            (
                link.id,
                link.source_entity_id,
                link.target_entity_id,
                link.link_type,
                link.confidence,
                link.evidence,
                json.dumps([link.source_modality, link.target_modality]),
                now,
            ),
        )
        saved_links.append(
            MultimodalEntityLinkResponse(
                link_id=link.id,
                source_entity_id=link.source_entity_id,
                target_entity_id=link.target_entity_id,
                source_modality=link.source_modality,
                target_modality=link.target_modality,
                link_type=link.link_type,
                confidence=link.confidence,
                evidence=link.evidence,
            )
        )

    conn.commit()
    conn.close()

    return MultimodalAlignmentResponse(
        project_id=project_id,
        aligned_count=len(saved_links),
        links=saved_links,
        message=f"Successfully aligned {len(saved_links)} cross-modal entity pairs",
    )


@app.get(
    "/api/v1/projects/{project_id}/multimodal/stats",
    response_model=MultimodalStatsResponse,
    tags=["Multimodal"],
)
async def get_multimodal_stats_endpoint(project_id: str, _=Depends(verify_api_key)):
    """
    获取项目多模态统计信息

    返回项目中视频、图片数量，以及跨模态实体关联统计
    """
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    conn = db.get_conn()

    # 统计视频数量
    video_count = conn.execute(
        "SELECT COUNT(*) as count FROM videos WHERE project_id = ?", (project_id,)
    ).fetchone()["count"]

    # 统计图片数量
    image_count = conn.execute(
        "SELECT COUNT(*) as count FROM images WHERE project_id = ?", (project_id,)
    ).fetchone()["count"]

    # 统计多模态实体提及
    multimodal_count = conn.execute(
        "SELECT COUNT(DISTINCT entity_id) as count FROM multimodal_mentions WHERE project_id = ?",
        (project_id,),
    ).fetchone()["count"]

    # 统计跨模态关联
    cross_modal_count = conn.execute(
        """SELECT COUNT(*) as count FROM multimodal_entity_links
           WHERE entity_id IN (SELECT id FROM entities WHERE project_id = ?)""",
        (project_id,),
    ).fetchone()["count"]

    # 模态分布
    modality_dist = {}
    for modality in ["audio", "video", "image", "document"]:
        count = conn.execute(
            "SELECT COUNT(*) as count FROM multimodal_mentions WHERE project_id = ? AND modality = ?",
            (project_id, modality),
        ).fetchone()["count"]
        modality_dist[modality] = count

    conn.close()

    return MultimodalStatsResponse(
        project_id=project_id,
        video_count=video_count,
        image_count=image_count,
        multimodal_entity_count=multimodal_count,
        cross_modal_links=cross_modal_count,
        modality_distribution=modality_dist,
    )


@app.get("/api/v1/projects/{project_id}/videos", tags=["Multimodal"])
async def list_project_videos_endpoint(project_id: str, _=Depends(verify_api_key)):
    """获取项目的视频列表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    conn = db.get_conn()

    videos = conn.execute(
        """SELECT id, filename, duration, fps, resolution,
                  full_ocr_text, status, created_at
           FROM videos WHERE project_id = ? ORDER BY created_at DESC""",
        (project_id,),
    ).fetchall()

    conn.close()

    return [
        {
            "id": v["id"],
            "filename": v["filename"],
            "duration": v["duration"],
            "fps": v["fps"],
            "resolution": json.loads(v["resolution"]) if v["resolution"] else None,
            "ocr_preview": (
                v["full_ocr_text"][:200] + "..."
                if v["full_ocr_text"] and len(v["full_ocr_text"]) > 200
                else v["full_ocr_text"]
            ),
            "status": v["status"],
            "created_at": v["created_at"],
        }
        for v in videos
    ]


@app.get("/api/v1/projects/{project_id}/images", tags=["Multimodal"])
async def list_project_images_endpoint(project_id: str, _=Depends(verify_api_key)):
    """获取项目的图片列表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    conn = db.get_conn()

    images = conn.execute(
        """SELECT id, filename, ocr_text, description,
                  extracted_entities, status, created_at
           FROM images WHERE project_id = ? ORDER BY created_at DESC""",
        (project_id,),
    ).fetchall()

    conn.close()

    return [
        {
            "id": img["id"],
            "filename": img["filename"],
            "ocr_preview": (
                img["ocr_text"][:200] + "..."
                if img["ocr_text"] and len(img["ocr_text"]) > 200
                else img["ocr_text"]
            ),
            "description": img["description"],
            "entity_count": len(json.loads(img["extracted_entities"]))
            if img["extracted_entities"]
            else 0,
            "status": img["status"],
            "created_at": img["created_at"],
        }
        for img in images
    ]


@app.get("/api/v1/videos/{video_id}/frames", tags=["Multimodal"])
async def get_video_frames_endpoint(video_id: str, _=Depends(verify_api_key)):
    """获取视频的关键帧列表"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    conn = db.get_conn()

    frames = conn.execute(
        """SELECT id, frame_number, timestamp, image_url, ocr_text, extracted_entities
           FROM video_frames WHERE video_id = ? ORDER BY timestamp""",
        (video_id,),
    ).fetchall()

    conn.close()

    return [
        {
            "id": f["id"],
            "frame_number": f["frame_number"],
            "timestamp": f["timestamp"],
            "image_url": f["image_url"],
            "ocr_text": f["ocr_text"],
            "entities": json.loads(f["extracted_entities"]) if f["extracted_entities"] else [],
        }
        for f in frames
    ]


@app.get("/api/v1/entities/{entity_id}/multimodal-mentions", tags=["Multimodal"])
async def get_entity_multimodal_mentions_endpoint(entity_id: str, _=Depends(verify_api_key)):
    """获取实体的多模态提及信息"""
    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    conn = db.get_conn()

    mentions = conn.execute(
        """SELECT m.*, e.name as entity_name
           FROM multimodal_mentions m
           JOIN entities e ON m.entity_id = e.id
           WHERE m.entity_id = ? ORDER BY m.created_at DESC""",
        (entity_id,),
    ).fetchall()

    conn.close()

    return [
        {
            "id": m["id"],
            "entity_id": m["entity_id"],
            "entity_name": m["entity_name"],
            "modality": m["modality"],
            "source_id": m["source_id"],
            "source_type": m["source_type"],
            "text_snippet": m["text_snippet"],
            "confidence": m["confidence"],
            "created_at": m["created_at"],
        }
        for m in mentions
    ]


@app.get("/api/v1/projects/{project_id}/multimodal/suggest-merges", tags=["Multimodal"])
async def suggest_multimodal_merges_endpoint(project_id: str, _=Depends(verify_api_key)):
    """
    建议多模态实体合并

    分析不同模态中的实体，建议可以合并的实体对
    """
    if not MULTIMODAL_LINKER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Multimodal entity linker not available")

    if not DB_AVAILABLE:
        raise HTTPException(status_code=500, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(project_id)
    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    # 获取所有实体
    entities = db.list_project_entities(project_id)
    entity_dicts = [
        {
            "id": e.id,
            "name": e.name,
            "type": e.type,
            "definition": e.definition,
            "aliases": e.aliases,
        }
        for e in entities
    ]

    # 获取现有链接
    conn = db.get_conn()
    existing_links = conn.execute(
        """SELECT * FROM multimodal_entity_links
           WHERE entity_id IN (SELECT id FROM entities WHERE project_id = ?)""",
        (project_id,),
    ).fetchall()
    conn.close()

    existing_link_objects = []
    for row in existing_links:
        existing_link_objects.append(
            EntityLink(
                id=row["id"],
                project_id=project_id,
                source_entity_id=row["entity_id"],
                target_entity_id=row["linked_entity_id"],
                link_type=row["link_type"],
                source_modality="unknown",
                target_modality="unknown",
                confidence=row["confidence"],
                evidence=row["evidence"] or "",
            )
        )

    # 获取建议
    linker = get_multimodal_entity_linker()
    suggestions = linker.suggest_entity_merges(entity_dicts, existing_link_objects)

    return {
        "project_id": project_id,
        "suggestion_count": len(suggestions),
        "suggestions": [
            {
                "entity1": {
                    "id": s["entity1"].get("id"),
                    "name": s["entity1"].get("name"),
                    "type": s["entity1"].get("type"),
                },
                "entity2": {
                    "id": s["entity2"].get("id"),
                    "name": s["entity2"].get("name"),
                    "type": s["entity2"].get("type"),
                },
                "similarity": s["similarity"],
                "match_type": s["match_type"],
                "suggested_action": s["suggested_action"],
            }
            for s in suggestions[:20]  # 最多返回20个建议
        ],
    }


# ==================== Phase 7: Multimodal Support API ====================


class VideoUploadResponse(BaseModel):
    video_id: str
    filename: str
    duration: float
    fps: float
    resolution: dict[str, int]
    frames_extracted: int
    audio_extracted: bool
    ocr_text_length: int
    status: str
    message: str


class ImageUploadResponse(BaseModel):
    image_id: str
    filename: str
    ocr_text_length: int
    description: str
    status: str
    message: str


class MultimodalEntityLinkResponse(BaseModel):
    link_id: str
    entity_id: str
    linked_entity_id: str
    link_type: str
    confidence: float
    evidence: str
    modalities: list[str]


class MultimodalProfileResponse(BaseModel):
    entity_id: str
    entity_name: str


# ==================== Phase 7 Task 7: Plugin Management Pydantic Models ====================


class PluginCreate(BaseModel):
    name: str = Field(..., description="插件名称")
    plugin_type: str = Field(
        ...,
        description="插件类型: chrome_extension, feishu_bot, dingtalk_bot, zapier, make, webdav, custom",
    )
    project_id: str = Field(..., description="关联项目ID")
    config: dict = Field(default_factory=dict, description="插件配置")


class PluginUpdate(BaseModel):
    name: str | None = None
    status: str | None = None  # active, inactive, error, pending
    config: dict | None = None


class PluginResponse(BaseModel):
    id: str
    name: str
    plugin_type: str
    project_id: str
    status: str
    config: dict
    created_at: str
    updated_at: str
    last_used_at: str | None
    use_count: int


class PluginListResponse(BaseModel):
    plugins: list[PluginResponse]
    total: int


class ChromeExtensionTokenCreate(BaseModel):
    name: str = Field(..., description="令牌名称")
    project_id: str | None = Field(default=None, description="关联项目ID")
    permissions: list[str] = Field(default=["read"], description="权限列表: read, write, delete")
    expires_days: int | None = Field(default=None, description="过期天数")


class ChromeExtensionTokenResponse(BaseModel):
    id: str
    token: str = Field(..., description="令牌（仅显示一次）")
    name: str
    project_id: str | None
    permissions: list[str]
    expires_at: str | None
    created_at: str


class ChromeExtensionImportRequest(BaseModel):
    token: str = Field(..., description="Chrome扩展令牌")
    url: str = Field(..., description="网页URL")
    title: str = Field(..., description="网页标题")
    content: str = Field(..., description="网页正文内容")
    html_content: str | None = Field(default=None, description="HTML内容（可选）")


class BotSessionCreate(BaseModel):
    session_id: str = Field(..., description="群ID或会话ID")
    session_name: str = Field(..., description="会话名称")
    project_id: str | None = Field(default=None, description="关联项目ID")
    webhook_url: str = Field(default="", description="Webhook URL")
    secret: str = Field(default="", description="签名密钥")


class BotSessionResponse(BaseModel):
    id: str
    bot_type: str
    session_id: str
    session_name: str
    project_id: str | None
    webhook_url: str
    is_active: bool
    created_at: str
    last_message_at: str | None
    message_count: int


class BotMessageRequest(BaseModel):
    session_id: str = Field(..., description="会话ID")
    msg_type: str = Field(default="text", description="消息类型: text, audio, file")
    content: dict = Field(default_factory=dict, description="消息内容")


class BotMessageResponse(BaseModel):
    success: bool
    response: str
    error: str | None = None


class WebhookEndpointCreate(BaseModel):
    name: str = Field(..., description="端点名称")
    endpoint_type: str = Field(..., description="端点类型: zapier, make, custom")
    endpoint_url: str = Field(..., description="Webhook URL")
    project_id: str | None = Field(default=None, description="关联项目ID")
    auth_type: str = Field(default="none", description="认证类型: none, api_key, oauth, custom")
    auth_config: dict = Field(default_factory=dict, description="认证配置")
    trigger_events: list[str] = Field(default_factory=list, description="触发事件列表")


class WebhookEndpointResponse(BaseModel):
    id: str
    name: str
    endpoint_type: str
    endpoint_url: str
    project_id: str | None
    auth_type: str
    trigger_events: list[str]
    is_active: bool
    created_at: str
    last_triggered_at: str | None
    trigger_count: int


class WebhookTestResponse(BaseModel):
    success: bool
    endpoint_id: str
    message: str


class WebDAVSyncCreate(BaseModel):
    name: str = Field(..., description="同步配置名称")
    project_id: str = Field(..., description="关联项目ID")
    server_url: str = Field(..., description="WebDAV服务器URL")
    username: str = Field(..., description="用户名")
    password: str = Field(..., description="密码")
    remote_path: str = Field(default="/insightflow", description="远程路径")
    sync_mode: str = Field(
        default="bidirectional", description="同步模式: bidirectional, upload_only, download_only"
    )
    sync_interval: int = Field(default=3600, description="同步间隔（秒）")


class WebDAVSyncResponse(BaseModel):
    id: str
    name: str
    project_id: str
    server_url: str
    username: str
    remote_path: str
    sync_mode: str
    sync_interval: int
    last_sync_at: str | None
    last_sync_status: str
    is_active: bool
    created_at: str
    sync_count: int


class WebDAVTestResponse(BaseModel):
    success: bool
    message: str


class WebDAVSyncResult(BaseModel):
    success: bool
    message: str
    entities_count: int | None = None
    relations_count: int | None = None
    remote_path: str | None = None
    error: str | None = None


# Plugin Manager singleton
_plugin_manager_instance = None


def get_plugin_manager_instance():
    global _plugin_manager_instance
    if _plugin_manager_instance is None and PLUGIN_MANAGER_AVAILABLE and DB_AVAILABLE:
        db = get_db_manager()
        _plugin_manager_instance = get_plugin_manager(db)
    return _plugin_manager_instance


# ==================== Phase 7 Task 7: Plugin Management Endpoints ====================


@app.post("/api/v1/plugins", response_model=PluginResponse, tags=["Plugins"])
async def create_plugin_endpoint(request: PluginCreate, _=Depends(verify_api_key)):
    """
    创建插件

    插件类型:
    - **chrome_extension**: Chrome 扩展
    - **feishu_bot**: 飞书机器人
    - **dingtalk_bot**: 钉钉机器人
    - **zapier**: Zapier 集成
    - **make**: Make (Integromat) 集成
    - **webdav**: WebDAV 同步
    - **custom**: 自定义插件
    """
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    plugin = Plugin(
        id=str(uuid.uuid4())[:8],
        name=request.name,
        plugin_type=request.plugin_type,
        project_id=request.project_id,
        config=request.config,
    )

    created = manager.create_plugin(plugin)

    return PluginResponse(
        id=created.id,
        name=created.name,
        plugin_type=created.plugin_type,
        project_id=created.project_id,
        status=created.status,
        config=created.config,
        created_at=created.created_at,
        updated_at=created.updated_at,
        last_used_at=created.last_used_at,
        use_count=created.use_count,
    )


@app.get("/api/v1/plugins", response_model=PluginListResponse, tags=["Plugins"])
async def list_plugins_endpoint(
    project_id: str | None = None,
    plugin_type: str | None = None,
    status: str | None = None,
    _=Depends(verify_api_key),
):
    """获取插件列表"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    plugins = manager.list_plugins(project_id, plugin_type, status)

    return PluginListResponse(
        plugins=[
            PluginResponse(
                id=p.id,
                name=p.name,
                plugin_type=p.plugin_type,
                project_id=p.project_id,
                status=p.status,
                config=p.config,
                created_at=p.created_at,
                updated_at=p.updated_at,
                last_used_at=p.last_used_at,
                use_count=p.use_count,
            )
            for p in plugins
        ],
        total=len(plugins),
    )


@app.get("/api/v1/plugins/{plugin_id}", response_model=PluginResponse, tags=["Plugins"])
async def get_plugin_endpoint(plugin_id: str, _=Depends(verify_api_key)):
    """获取插件详情"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    plugin = manager.get_plugin(plugin_id)

    if not plugin:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return PluginResponse(
        id=plugin.id,
        name=plugin.name,
        plugin_type=plugin.plugin_type,
        project_id=plugin.project_id,
        status=plugin.status,
        config=plugin.config,
        created_at=plugin.created_at,
        updated_at=plugin.updated_at,
        last_used_at=plugin.last_used_at,
        use_count=plugin.use_count,
    )


@app.patch("/api/v1/plugins/{plugin_id}", response_model=PluginResponse, tags=["Plugins"])
async def update_plugin_endpoint(plugin_id: str, request: PluginUpdate, _=Depends(verify_api_key)):
    """更新插件"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    update_data = {k: v for k, v in request.dict().items() if v is not None}
    updated = manager.update_plugin(plugin_id, **update_data)

    if not updated:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return PluginResponse(
        id=updated.id,
        name=updated.name,
        plugin_type=updated.plugin_type,
        project_id=updated.project_id,
        status=updated.status,
        config=updated.config,
        created_at=updated.created_at,
        updated_at=updated.updated_at,
        last_used_at=updated.last_used_at,
        use_count=updated.use_count,
    )


@app.delete("/api/v1/plugins/{plugin_id}", tags=["Plugins"])
async def delete_plugin_endpoint(plugin_id: str, _=Depends(verify_api_key)):
    """删除插件"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    success = manager.delete_plugin(plugin_id)

    if not success:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return {"success": True, "message": "Plugin deleted successfully"}


# ==================== Phase 7 Task 7: Chrome Extension Endpoints ====================


@app.post(
    "/api/v1/plugins/chrome/tokens",
    response_model=ChromeExtensionTokenResponse,
    tags=["Chrome Extension"],
)
async def create_chrome_token_endpoint(
    request: ChromeExtensionTokenCreate, _=Depends(verify_api_key)
):
    """
    创建 Chrome 扩展令牌

    用于 Chrome 扩展验证和授权
    """
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.CHROME_EXTENSION)

    if not handler:
        raise HTTPException(status_code=503, detail="Chrome extension handler not available")

    token = handler.create_token(
        name=request.name,
        project_id=request.project_id,
        permissions=request.permissions,
        expires_days=request.expires_days,
    )

    return ChromeExtensionTokenResponse(
        id=token.id,
        token=token.token,
        name=token.name,
        project_id=token.project_id,
        permissions=token.permissions,
        expires_at=token.expires_at,
        created_at=token.created_at,
    )


@app.get("/api/v1/plugins/chrome/tokens", tags=["Chrome Extension"])
async def list_chrome_tokens_endpoint(project_id: str | None = None, _=Depends(verify_api_key)):
    """列出 Chrome 扩展令牌"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.CHROME_EXTENSION)

    if not handler:
        raise HTTPException(status_code=503, detail="Chrome extension handler not available")

    tokens = handler.list_tokens(project_id=project_id)

    return {
        "tokens": [
            {
                "id": t.id,
                "name": t.name,
                "project_id": t.project_id,
                "permissions": t.permissions,
                "expires_at": t.expires_at,
                "created_at": t.created_at,
                "last_used_at": t.last_used_at,
                "use_count": t.use_count,
                "is_revoked": t.is_revoked,
            }
            for t in tokens
        ],
        "total": len(tokens),
    }


@app.delete("/api/v1/plugins/chrome/tokens/{token_id}", tags=["Chrome Extension"])
async def revoke_chrome_token_endpoint(token_id: str, _=Depends(verify_api_key)):
    """撤销 Chrome 扩展令牌"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.CHROME_EXTENSION)

    if not handler:
        raise HTTPException(status_code=503, detail="Chrome extension handler not available")

    success = handler.revoke_token(token_id)

    if not success:
        raise HTTPException(status_code=404, detail="Token not found")

    return {"success": True, "message": "Token revoked successfully"}


@app.post("/api/v1/plugins/chrome/import", tags=["Chrome Extension"])
async def chrome_import_webpage_endpoint(request: ChromeExtensionImportRequest):
    """
    Chrome 扩展导入网页内容

    无需 API Key，使用 Chrome 扩展令牌验证
    """
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.CHROME_EXTENSION)

    if not handler:
        raise HTTPException(status_code=503, detail="Chrome extension handler not available")

    # 验证令牌
    token = handler.validate_token(request.token)
    if not token:
        raise HTTPException(status_code=401, detail="Invalid or expired token")

    # 导入网页
    result = await handler.import_webpage(
        token=token,
        url=request.url,
        title=request.title,
        content=request.content,
        html_content=request.html_content,
    )

    if not result["success"]:
        raise HTTPException(status_code=400, detail=result.get("error", "Import failed"))

    return result


# ==================== Phase 7 Task 7: Bot Endpoints ====================


@app.post("/api/v1/plugins/bot/feishu/sessions", response_model=BotSessionResponse, tags=["Bot"])
async def create_feishu_session_endpoint(request: BotSessionCreate, _=Depends(verify_api_key)):
    """创建飞书机器人会话"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.FEISHU_BOT)

    if not handler:
        raise HTTPException(status_code=503, detail="Feishu bot handler not available")

    session = handler.create_session(
        session_id=request.session_id,
        session_name=request.session_name,
        project_id=request.project_id,
        webhook_url=request.webhook_url,
        secret=request.secret,
    )

    return BotSessionResponse(
        id=session.id,
        bot_type=session.bot_type,
        session_id=session.session_id,
        session_name=session.session_name,
        project_id=session.project_id,
        webhook_url=session.webhook_url,
        is_active=session.is_active,
        created_at=session.created_at,
        last_message_at=session.last_message_at,
        message_count=session.message_count,
    )


@app.post("/api/v1/plugins/bot/dingtalk/sessions", response_model=BotSessionResponse, tags=["Bot"])
async def create_dingtalk_session_endpoint(request: BotSessionCreate, _=Depends(verify_api_key)):
    """创建钉钉机器人会话"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.DINGTALK_BOT)

    if not handler:
        raise HTTPException(status_code=503, detail="DingTalk bot handler not available")

    session = handler.create_session(
        session_id=request.session_id,
        session_name=request.session_name,
        project_id=request.project_id,
        webhook_url=request.webhook_url,
        secret=request.secret,
    )

    return BotSessionResponse(
        id=session.id,
        bot_type=session.bot_type,
        session_id=session.session_id,
        session_name=session.session_name,
        project_id=session.project_id,
        webhook_url=session.webhook_url,
        is_active=session.is_active,
        created_at=session.created_at,
        last_message_at=session.last_message_at,
        message_count=session.message_count,
    )


@app.get("/api/v1/plugins/bot/{bot_type}/sessions", tags=["Bot"])
async def list_bot_sessions_endpoint(
    bot_type: str, project_id: str | None = None, _=Depends(verify_api_key)
):
    """列出机器人会话"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    if bot_type == "feishu":
        handler = manager.get_handler(PluginType.FEISHU_BOT)
    elif bot_type == "dingtalk":
        handler = manager.get_handler(PluginType.DINGTALK_BOT)
    else:
        raise HTTPException(status_code=400, detail="Invalid bot type. Must be feishu or dingtalk")

    if not handler:
        raise HTTPException(status_code=503, detail=f"{bot_type} bot handler not available")

    sessions = handler.list_sessions(project_id=project_id)

    return {
        "sessions": [
            {
                "id": s.id,
                "bot_type": s.bot_type,
                "session_id": s.session_id,
                "session_name": s.session_name,
                "project_id": s.project_id,
                "is_active": s.is_active,
                "created_at": s.created_at,
                "last_message_at": s.last_message_at,
                "message_count": s.message_count,
            }
            for s in sessions
        ],
        "total": len(sessions),
    }


@app.post("/api/v1/plugins/bot/{bot_type}/webhook", tags=["Bot"])
async def bot_webhook_endpoint(bot_type: str, request: Request):
    """
    机器人 Webhook 接收端点

    接收飞书/钉钉机器人的消息
    """
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    if bot_type == "feishu":
        handler = manager.get_handler(PluginType.FEISHU_BOT)
    elif bot_type == "dingtalk":
        handler = manager.get_handler(PluginType.DINGTALK_BOT)
    else:
        raise HTTPException(status_code=400, detail="Invalid bot type")

    if not handler:
        raise HTTPException(status_code=503, detail=f"{bot_type} bot handler not available")

    # 获取消息内容
    message = await request.json()

    # 获取会话ID（飞书和钉钉的格式不同）
    if bot_type == "feishu":
        session_id = message.get("chat_id") or message.get("open_chat_id")
    else:  # dingtalk
        session_id = message.get("conversationId") or message.get("senderStaffId")

    if not session_id:
        raise HTTPException(status_code=400, detail="Cannot identify session")

    # 获取会话
    session = handler.get_session(session_id)
    if not session:
        # 自动创建会话
        session = handler.create_session(
            session_id=session_id, session_name=f"Auto-{session_id[:8]}", webhook_url=""
        )

    # 处理消息
    result = await handler.handle_message(session, message)

    # 如果配置了 webhook，发送回复
    if session.webhook_url and result.get("response"):
        await handler.send_message(session, result["response"])

    return result


@app.post("/api/v1/plugins/bot/{bot_type}/sessions/{session_id}/send", tags=["Bot"])
async def send_bot_message_endpoint(
    bot_type: str, session_id: str, message: str, _=Depends(verify_api_key)
):
    """发送消息到机器人会话"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    if bot_type == "feishu":
        handler = manager.get_handler(PluginType.FEISHU_BOT)
    elif bot_type == "dingtalk":
        handler = manager.get_handler(PluginType.DINGTALK_BOT)
    else:
        raise HTTPException(status_code=400, detail="Invalid bot type")

    if not handler:
        raise HTTPException(status_code=503, detail=f"{bot_type} bot handler not available")

    session = handler.get_session(session_id)
    if not session:
        raise HTTPException(status_code=404, detail="Session not found")

    success = await handler.send_message(session, message)

    return {"success": success, "message": "Message sent" if success else "Failed to send message"}


# ==================== Phase 7 Task 7: Integration Endpoints ====================


@app.post(
    "/api/v1/plugins/integrations/zapier",
    response_model=WebhookEndpointResponse,
    tags=["Integrations"],
)
async def create_zapier_endpoint(request: WebhookEndpointCreate, _=Depends(verify_api_key)):
    """创建 Zapier Webhook 端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.ZAPIER)

    if not handler:
        raise HTTPException(status_code=503, detail="Zapier handler not available")

    endpoint = handler.create_endpoint(
        name=request.name,
        endpoint_url=request.endpoint_url,
        project_id=request.project_id,
        auth_type=request.auth_type,
        auth_config=request.auth_config,
        trigger_events=request.trigger_events,
    )

    return WebhookEndpointResponse(
        id=endpoint.id,
        name=endpoint.name,
        endpoint_type=endpoint.endpoint_type,
        endpoint_url=endpoint.endpoint_url,
        project_id=endpoint.project_id,
        auth_type=endpoint.auth_type,
        trigger_events=endpoint.trigger_events,
        is_active=endpoint.is_active,
        created_at=endpoint.created_at,
        last_triggered_at=endpoint.last_triggered_at,
        trigger_count=endpoint.trigger_count,
    )


@app.post(
    "/api/v1/plugins/integrations/make",
    response_model=WebhookEndpointResponse,
    tags=["Integrations"],
)
async def create_make_endpoint(request: WebhookEndpointCreate, _=Depends(verify_api_key)):
    """创建 Make (Integromat) Webhook 端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.MAKE)

    if not handler:
        raise HTTPException(status_code=503, detail="Make handler not available")

    endpoint = handler.create_endpoint(
        name=request.name,
        endpoint_url=request.endpoint_url,
        project_id=request.project_id,
        auth_type=request.auth_type,
        auth_config=request.auth_config,
        trigger_events=request.trigger_events,
    )

    return WebhookEndpointResponse(
        id=endpoint.id,
        name=endpoint.name,
        endpoint_type=endpoint.endpoint_type,
        endpoint_url=endpoint.endpoint_url,
        project_id=endpoint.project_id,
        auth_type=endpoint.auth_type,
        trigger_events=endpoint.trigger_events,
        is_active=endpoint.is_active,
        created_at=endpoint.created_at,
        last_triggered_at=endpoint.last_triggered_at,
        trigger_count=endpoint.trigger_count,
    )


@app.get("/api/v1/plugins/integrations/{endpoint_type}", tags=["Integrations"])
async def list_integration_endpoints_endpoint(
    endpoint_type: str, project_id: str | None = None, _=Depends(verify_api_key)
):
    """列出集成端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    if endpoint_type == "zapier":
        handler = manager.get_handler(PluginType.ZAPIER)
    elif endpoint_type == "make":
        handler = manager.get_handler(PluginType.MAKE)
    else:
        raise HTTPException(status_code=400, detail="Invalid endpoint type")

    if not handler:
        raise HTTPException(status_code=503, detail=f"{endpoint_type} handler not available")

    endpoints = handler.list_endpoints(project_id=project_id)

    return {
        "endpoints": [
            {
                "id": e.id,
                "name": e.name,
                "endpoint_type": e.endpoint_type,
                "endpoint_url": e.endpoint_url,
                "project_id": e.project_id,
                "auth_type": e.auth_type,
                "trigger_events": e.trigger_events,
                "is_active": e.is_active,
                "created_at": e.created_at,
                "last_triggered_at": e.last_triggered_at,
                "trigger_count": e.trigger_count,
            }
            for e in endpoints
        ],
        "total": len(endpoints),
    }


@app.post(
    "/api/v1/plugins/integrations/{endpoint_id}/test",
    response_model=WebhookTestResponse,
    tags=["Integrations"],
)
async def test_integration_endpoint(endpoint_id: str, _=Depends(verify_api_key)):
    """测试集成端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    # 尝试获取端点（可能是 Zapier 或 Make）
    handler = manager.get_handler(PluginType.ZAPIER)
    endpoint = handler.get_endpoint(endpoint_id) if handler else None

    if not endpoint:
        handler = manager.get_handler(PluginType.MAKE)
        endpoint = handler.get_endpoint(endpoint_id) if handler else None

    if not endpoint:
        raise HTTPException(status_code=404, detail="Endpoint not found")

    result = await handler.test_endpoint(endpoint)

    return WebhookTestResponse(
        success=result["success"], endpoint_id=endpoint_id, message=result["message"]
    )


@app.post("/api/v1/plugins/integrations/{endpoint_id}/trigger", tags=["Integrations"])
async def trigger_integration_endpoint(
    endpoint_id: str, event_type: str, data: dict, _=Depends(verify_api_key)
):
    """手动触发集成端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()

    # 尝试获取端点（可能是 Zapier 或 Make）
    handler = manager.get_handler(PluginType.ZAPIER)
    endpoint = handler.get_endpoint(endpoint_id) if handler else None

    if not endpoint:
        handler = manager.get_handler(PluginType.MAKE)
        endpoint = handler.get_endpoint(endpoint_id) if handler else None

    if not endpoint:
        raise HTTPException(status_code=404, detail="Endpoint not found")

    success = await handler.trigger(endpoint, event_type, data)

    return {
        "success": success,
        "message": "Triggered successfully" if success else "Trigger failed",
    }


# ==================== Phase 7 Task 7: WebDAV Endpoints ====================


@app.post("/api/v1/plugins/webdav", response_model=WebDAVSyncResponse, tags=["WebDAV"])
async def create_webdav_sync_endpoint(request: WebDAVSyncCreate, _=Depends(verify_api_key)):
    """
    创建 WebDAV 同步配置

    支持与坚果云等 WebDAV 网盘同步项目数据
    """
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.WEBDAV)

    if not handler:
        raise HTTPException(status_code=503, detail="WebDAV handler not available")

    sync = handler.create_sync(
        name=request.name,
        project_id=request.project_id,
        server_url=request.server_url,
        username=request.username,
        password=request.password,
        remote_path=request.remote_path,
        sync_mode=request.sync_mode,
        sync_interval=request.sync_interval,
    )

    return WebDAVSyncResponse(
        id=sync.id,
        name=sync.name,
        project_id=sync.project_id,
        server_url=sync.server_url,
        username=sync.username,
        remote_path=sync.remote_path,
        sync_mode=sync.sync_mode,
        sync_interval=sync.sync_interval,
        last_sync_at=sync.last_sync_at,
        last_sync_status=sync.last_sync_status,
        is_active=sync.is_active,
        created_at=sync.created_at,
        sync_count=sync.sync_count,
    )


@app.get("/api/v1/plugins/webdav", tags=["WebDAV"])
async def list_webdav_syncs_endpoint(project_id: str | None = None, _=Depends(verify_api_key)):
    """列出 WebDAV 同步配置"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.WEBDAV)

    if not handler:
        raise HTTPException(status_code=503, detail="WebDAV handler not available")

    syncs = handler.list_syncs(project_id=project_id)

    return {
        "syncs": [
            {
                "id": s.id,
                "name": s.name,
                "project_id": s.project_id,
                "server_url": s.server_url,
                "username": s.username,
                "remote_path": s.remote_path,
                "sync_mode": s.sync_mode,
                "sync_interval": s.sync_interval,
                "last_sync_at": s.last_sync_at,
                "last_sync_status": s.last_sync_status,
                "is_active": s.is_active,
                "created_at": s.created_at,
                "sync_count": s.sync_count,
            }
            for s in syncs
        ],
        "total": len(syncs),
    }


@app.post(
    "/api/v1/plugins/webdav/{sync_id}/test", response_model=WebDAVTestResponse, tags=["WebDAV"]
)
async def test_webdav_connection_endpoint(sync_id: str, _=Depends(verify_api_key)):
    """测试 WebDAV 连接"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.WEBDAV)

    if not handler:
        raise HTTPException(status_code=503, detail="WebDAV handler not available")

    sync = handler.get_sync(sync_id)
    if not sync:
        raise HTTPException(status_code=404, detail="Sync configuration not found")

    result = await handler.test_connection(sync)

    return WebDAVTestResponse(
        success=result["success"],
        message=result.get("message") or result.get("error", "Unknown result"),
    )


@app.post("/api/v1/plugins/webdav/{sync_id}/sync", response_model=WebDAVSyncResult, tags=["WebDAV"])
async def sync_webdav_endpoint(sync_id: str, _=Depends(verify_api_key)):
    """执行 WebDAV 同步"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.WEBDAV)

    if not handler:
        raise HTTPException(status_code=503, detail="WebDAV handler not available")

    sync = handler.get_sync(sync_id)
    if not sync:
        raise HTTPException(status_code=404, detail="Sync configuration not found")

    result = await handler.sync_project(sync)

    return WebDAVSyncResult(
        success=result["success"],
        message=result.get("message") or result.get("error", "Sync completed"),
        entities_count=result.get("entities_count"),
        relations_count=result.get("relations_count"),
        remote_path=result.get("remote_path"),
        error=result.get("error"),
    )


@app.delete("/api/v1/plugins/webdav/{sync_id}", tags=["WebDAV"])
async def delete_webdav_sync_endpoint(sync_id: str, _=Depends(verify_api_key)):
    """删除 WebDAV 同步配置"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager_instance()
    handler = manager.get_handler(PluginType.WEBDAV)

    if not handler:
        raise HTTPException(status_code=503, detail="WebDAV handler not available")

    success = handler.delete_sync(sync_id)

    if not success:
        raise HTTPException(status_code=404, detail="Sync configuration not found")

    return {"success": True, "message": "WebDAV sync configuration deleted"}


@app.get("/api/v1/openapi.json", include_in_schema=False)
async def get_openapi():
    """获取 OpenAPI 规范"""
    from fastapi.openapi.utils import get_openapi

    return get_openapi(
        title=app.title,
        version=app.version,
        description=app.description,
        routes=app.routes,
        tags=app.openapi_tags,
    )


# Serve frontend - MUST be last to not override API routes
app.mount("/", StaticFiles(directory="frontend", html=True), name="frontend")

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)


class PluginCreateRequest(BaseModel):
    name: str
    plugin_type: str
    project_id: str | None = None
    config: dict | None = {}


class PluginResponse(BaseModel):
    id: str
    name: str
    plugin_type: str
    project_id: str | None
    status: str
    api_key: str
    created_at: str


class BotSessionResponse(BaseModel):
    id: str
    plugin_id: str
    platform: str
    session_id: str
    user_id: str | None
    user_name: str | None
    project_id: str | None
    message_count: int
    created_at: str
    last_message_at: str | None


class WebhookEndpointResponse(BaseModel):
    id: str
    plugin_id: str
    name: str
    endpoint_path: str
    endpoint_type: str
    target_project_id: str | None
    is_active: bool
    trigger_count: int
    created_at: str


class WebDAVSyncResponse(BaseModel):
    id: str
    plugin_id: str
    name: str
    server_url: str
    username: str
    remote_path: str
    local_path: str
    sync_direction: str
    sync_mode: str
    auto_analyze: bool
    is_active: bool
    last_sync_at: str | None
    created_at: str


class ChromeClipRequest(BaseModel):
    url: str
    title: str
    content: str
    content_type: str = "page"
    meta: dict | None = {}
    project_id: str | None = None


class ChromeClipResponse(BaseModel):
    clip_id: str
    project_id: str
    url: str
    title: str
    status: str
    message: str


class BotMessagePayload(BaseModel):
    platform: str
    session_id: str
    user_id: str | None = None
    user_name: str | None = None
    message_type: str
    content: str
    project_id: str | None = None


class BotMessageResult(BaseModel):
    success: bool
    reply: str | None = None
    session_id: str
    action: str | None = None


class WebhookPayload(BaseModel):
    event: str
    data: dict


@app.post("/api/v1/plugins", response_model=PluginResponse, tags=["Plugins"])
async def create_plugin(request: PluginCreateRequest, api_key: str = Depends(verify_api_key)):
    """创建插件"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    plugin = manager.create_plugin(
        name=request.name,
        plugin_type=request.plugin_type,
        project_id=request.project_id,
        config=request.config,
    )

    return PluginResponse(
        id=plugin.id,
        name=plugin.name,
        plugin_type=plugin.plugin_type,
        project_id=plugin.project_id,
        status=plugin.status,
        api_key=plugin.api_key,
        created_at=plugin.created_at,
    )


@app.get("/api/v1/plugins", tags=["Plugins"])
async def list_plugins(
    project_id: str | None = None,
    plugin_type: str | None = None,
    api_key: str = Depends(verify_api_key),
):
    """列出插件"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    plugins = manager.list_plugins(project_id=project_id, plugin_type=plugin_type)

    return {
        "plugins": [
            {
                "id": p.id,
                "name": p.name,
                "plugin_type": p.plugin_type,
                "project_id": p.project_id,
                "status": p.status,
                "use_count": p.use_count,
                "created_at": p.created_at,
            }
            for p in plugins
        ]
    }


@app.get("/api/v1/plugins/{plugin_id}", response_model=PluginResponse, tags=["Plugins"])
async def get_plugin(plugin_id: str, api_key: str = Depends(verify_api_key)):
    """获取插件详情"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    plugin = manager.get_plugin(plugin_id)

    if not plugin:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return PluginResponse(
        id=plugin.id,
        name=plugin.name,
        plugin_type=plugin.plugin_type,
        project_id=plugin.project_id,
        status=plugin.status,
        api_key=plugin.api_key,
        created_at=plugin.created_at,
    )


@app.delete("/api/v1/plugins/{plugin_id}", tags=["Plugins"])
async def delete_plugin(plugin_id: str, api_key: str = Depends(verify_api_key)):
    """删除插件"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    manager.delete_plugin(plugin_id)

    return {"success": True, "message": "Plugin deleted"}


@app.post("/api/v1/plugins/{plugin_id}/regenerate-key", tags=["Plugins"])
async def regenerate_plugin_key(plugin_id: str, api_key: str = Depends(verify_api_key)):
    """重新生成插件 API Key"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    new_key = manager.regenerate_api_key(plugin_id)

    return {"success": True, "api_key": new_key}


# ==================== Chrome Extension API ====================


@app.post(
    "/api/v1/plugins/chrome/clip", response_model=ChromeClipResponse, tags=["Chrome Extension"]
)
async def chrome_clip(
    request: ChromeClipRequest, x_api_key: str | None = Header(None, alias="X-API-Key")
):
    """Chrome 插件保存网页内容"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    if not x_api_key:
        raise HTTPException(status_code=401, detail="API Key required")

    manager = get_plugin_manager()
    plugin = manager.get_plugin_by_api_key(x_api_key)

    if not plugin or plugin.plugin_type != "chrome_extension":
        raise HTTPException(status_code=401, detail="Invalid API Key")

    # 确定目标项目
    project_id = request.project_id or plugin.project_id
    if not project_id:
        raise HTTPException(status_code=400, detail="Project ID required")

    # 创建转录记录（将网页内容作为文档处理）
    db = get_db_manager()

    # 生成文档内容
    doc_content = f"""# {request.title}

URL: {request.url}

## 内容

{request.content}

## 元数据

{json.dumps(request.meta, ensure_ascii=False, indent=2)}
"""

    # 创建转录记录
    transcript_id = db.create_transcript(
        project_id=project_id,
        filename=f"clip_{request.title[:50]}.md",
        full_text=doc_content,
        transcript_type="document",
    )

    # 记录活动
    manager.log_activity(
        plugin_id=plugin.id,
        activity_type="clip",
        source="chrome_extension",
        details={
            "url": request.url,
            "title": request.title,
            "project_id": project_id,
            "transcript_id": transcript_id,
        },
    )

    return ChromeClipResponse(
        clip_id=str(uuid.uuid4()),
        project_id=project_id,
        url=request.url,
        title=request.title,
        status="success",
        message="Content saved successfully",
    )


# ==================== Bot API ====================


@app.post("/api/v1/bots/webhook/{platform}", response_model=BotMessageResponse, tags=["Bot"])
async def bot_webhook(
    platform: str, request: Request, x_signature: str | None = Header(None, alias="X-Signature")
):
    """接收机器人 Webhook 消息（飞书/钉钉/Slack）"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    body = await request.body()
    payload = json.loads(body)

    manager = get_plugin_manager()
    handler = BotHandler(manager)

    # 解析消息
    if platform == "feishu":
        message = handler.parse_feishu_message(payload)
    elif platform == "dingtalk":
        message = handler.parse_dingtalk_message(payload)
    elif platform == "slack":
        message = handler.parse_slack_message(payload)
    else:
        raise HTTPException(status_code=400, detail=f"Unsupported platform: {platform}")

    # 查找或创建会话
    # 这里简化处理，实际应该根据 plugin_id 查找
    # 暂时返回简单的回复

    return BotMessageResponse(
        success=True,
        reply="收到消息！请使用 InsightFlow 控制台查看更多功能。",
        session_id=message.get("session_id", ""),
        action="reply",
    )


@app.get("/api/v1/bots/sessions", response_model=list[BotSessionResponse], tags=["Bot"])
async def list_bot_sessions(
    plugin_id: str | None = None,
    project_id: str | None = None,
    api_key: str = Depends(verify_api_key),
):
    """列出机器人会话"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    sessions = manager.list_bot_sessions(plugin_id=plugin_id, project_id=project_id)

    return [
        BotSessionResponse(
            id=s.id,
            plugin_id=s.plugin_id,
            platform=s.platform,
            session_id=s.session_id,
            user_id=s.user_id,
            user_name=s.user_name,
            project_id=s.project_id,
            message_count=s.message_count,
            created_at=s.created_at,
            last_message_at=s.last_message_at,
        )
        for s in sessions
    ]


# ==================== Webhook Integration API ====================


@app.post(
    "/api/v1/webhook-endpoints", response_model=WebhookEndpointResponse, tags=["Integrations"]
)
async def create_integration_webhook_endpoint(
    plugin_id: str,
    name: str,
    endpoint_type: str,
    target_project_id: str | None = None,
    allowed_events: list[str] | None = None,
    api_key: str = Depends(verify_api_key),
):
    """创建 Webhook 端点（用于 Zapier/Make 集成）"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    endpoint = manager.create_webhook_endpoint(
        plugin_id=plugin_id,
        name=name,
        endpoint_type=endpoint_type,
        target_project_id=target_project_id,
        allowed_events=allowed_events,
    )

    return WebhookEndpointResponse(
        id=endpoint.id,
        plugin_id=endpoint.plugin_id,
        name=endpoint.name,
        endpoint_path=endpoint.endpoint_path,
        endpoint_type=endpoint.endpoint_type,
        target_project_id=endpoint.target_project_id,
        is_active=endpoint.is_active,
        trigger_count=endpoint.trigger_count,
        created_at=endpoint.created_at,
    )


@app.get(
    "/api/v1/webhook-endpoints", response_model=list[WebhookEndpointResponse], tags=["Integrations"]
)
async def list_webhook_endpoints(
    plugin_id: str | None = None, api_key: str = Depends(verify_api_key)
):
    """列出 Webhook 端点"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    endpoints = manager.list_webhook_endpoints(plugin_id=plugin_id)

    return [
        WebhookEndpointResponse(
            id=e.id,
            plugin_id=e.plugin_id,
            name=e.name,
            endpoint_path=e.endpoint_path,
            endpoint_type=e.endpoint_type,
            target_project_id=e.target_project_id,
            is_active=e.is_active,
            trigger_count=e.trigger_count,
            created_at=e.created_at,
        )
        for e in endpoints
    ]


@app.post("/webhook/{endpoint_type}/{token}", tags=["Integrations"])
async def receive_webhook(
    endpoint_type: str,
    token: str,
    request: Request,
    x_signature: str | None = Header(None, alias="X-Signature"),
):
    """接收外部 Webhook 调用（Zapier/Make/Custom）"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()

    # 构建完整路径查找端点
    path = f"/webhook/{endpoint_type}/{token}"
    endpoint = manager.get_webhook_endpoint_by_path(path)

    if not endpoint or not endpoint.is_active:
        raise HTTPException(status_code=404, detail="Webhook endpoint not found")

    # 验证签名（如果有）
    if endpoint.secret and x_signature:
        body = await request.body()
        integration = WebhookIntegration(manager)
        if not integration.validate_signature(body, x_signature, endpoint.secret):
            raise HTTPException(status_code=401, detail="Invalid signature")

    # 解析请求体
    body = await request.json()

    # 更新触发统计
    manager.update_webhook_trigger(endpoint.id)

    # 记录活动
    manager.log_activity(
        plugin_id=endpoint.plugin_id,
        activity_type="webhook",
        source=endpoint_type,
        details={
            "endpoint_id": endpoint.id,
            "event": body.get("event"),
            "data_keys": list(body.get("data", {}).keys()),
        },
    )

    # 处理数据（简化版本）
    # 实际应该根据 endpoint.target_project_id 和 body 内容创建文档/实体等

    return {"success": True, "endpoint_id": endpoint.id, "received_at": datetime.now().isoformat()}


# ==================== WebDAV API ====================


@app.post("/api/v1/webdav-syncs", response_model=WebDAVSyncResponse, tags=["WebDAV"])
async def create_webdav_sync(
    plugin_id: str,
    name: str,
    server_url: str,
    username: str,
    password: str,
    remote_path: str = "/",
    local_path: str = "./sync",
    sync_direction: str = "bidirectional",
    sync_mode: str = "manual",
    auto_analyze: bool = True,
    api_key: str = Depends(verify_api_key),
):
    """创建 WebDAV 同步配置"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    sync = manager.create_webdav_sync(
        plugin_id=plugin_id,
        name=name,
        server_url=server_url,
        username=username,
        password=password,
        remote_path=remote_path,
        local_path=local_path,
        sync_direction=sync_direction,
        sync_mode=sync_mode,
        auto_analyze=auto_analyze,
    )

    return WebDAVSyncResponse(
        id=sync.id,
        plugin_id=sync.plugin_id,
        name=sync.name,
        server_url=sync.server_url,
        username=sync.username,
        remote_path=sync.remote_path,
        local_path=sync.local_path,
        sync_direction=sync.sync_direction,
        sync_mode=sync.sync_mode,
        auto_analyze=sync.auto_analyze,
        is_active=sync.is_active,
        last_sync_at=sync.last_sync_at,
        created_at=sync.created_at,
    )


@app.get("/api/v1/webdav-syncs", response_model=list[WebDAVSyncResponse], tags=["WebDAV"])
async def list_webdav_syncs(plugin_id: str | None = None, api_key: str = Depends(verify_api_key)):
    """列出 WebDAV 同步配置"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    syncs = manager.list_webdav_syncs(plugin_id=plugin_id)

    return [
        WebDAVSyncResponse(
            id=s.id,
            plugin_id=s.plugin_id,
            name=s.name,
            server_url=s.server_url,
            username=s.username,
            remote_path=s.remote_path,
            local_path=s.local_path,
            sync_direction=s.sync_direction,
            sync_mode=s.sync_mode,
            auto_analyze=s.auto_analyze,
            is_active=s.is_active,
            last_sync_at=s.last_sync_at,
            created_at=s.created_at,
        )
        for s in syncs
    ]


@app.post("/api/v1/webdav-syncs/{sync_id}/test", tags=["WebDAV"])
async def test_webdav_connection(sync_id: str, api_key: str = Depends(verify_api_key)):
    """测试 WebDAV 连接"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    sync = manager.get_webdav_sync(sync_id)

    if not sync:
        raise HTTPException(status_code=404, detail="WebDAV sync not found")

    from plugin_manager import WebDAVSync as WebDAVSyncHandler

    handler = WebDAVSyncHandler(manager)

    success, message = await handler.test_connection(sync.server_url, sync.username, sync.password)

    return {"success": success, "message": message}


@app.post("/api/v1/webdav-syncs/{sync_id}/sync", tags=["WebDAV"])
async def trigger_webdav_sync(sync_id: str, api_key: str = Depends(verify_api_key)):
    """手动触发 WebDAV 同步"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    sync = manager.get_webdav_sync(sync_id)

    if not sync:
        raise HTTPException(status_code=404, detail="WebDAV sync not found")

    # 这里应该启动异步同步任务
    # 简化版本，仅返回成功

    manager.update_webdav_sync(
        sync_id, last_sync_at=datetime.now().isoformat(), last_sync_status="running"
    )

    return {"success": True, "sync_id": sync_id, "status": "running", "message": "Sync started"}


# ==================== Plugin Activity Logs ====================


@app.get("/api/v1/plugins/{plugin_id}/logs", tags=["Plugins"])
async def get_plugin_logs(
    plugin_id: str,
    activity_type: str | None = None,
    limit: int = 100,
    api_key: str = Depends(verify_api_key),
):
    """获取插件活动日志"""
    if not PLUGIN_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Plugin manager not available")

    manager = get_plugin_manager()
    logs = manager.get_activity_logs(plugin_id=plugin_id, activity_type=activity_type, limit=limit)

    return {
        "logs": [
            {
                "id": log.id,
                "activity_type": log.activity_type,
                "source": log.source,
                "details": log.details,
                "created_at": log.created_at,
            }
            for log in logs
        ]
    }


# ==================== Phase 7 Task 3: Security & Compliance API ====================


# Pydantic models for security API
class AuditLogResponse(BaseModel):
    id: str
    action_type: str
    user_id: str | None = None
    user_ip: str | None = None
    resource_type: str | None = None
    resource_id: str | None = None
    action_details: str | None = None
    success: bool = True
    error_message: str | None = None
    created_at: str


class AuditStatsResponse(BaseModel):
    total_actions: int
    success_count: int
    failure_count: int
    action_breakdown: dict[str, dict[str, int]]


class EncryptionEnableRequest(BaseModel):
    master_password: str


class EncryptionConfigResponse(BaseModel):
    id: str
    project_id: str
    is_enabled: bool
    encryption_type: str
    created_at: str
    updated_at: str


class MaskingRuleCreateRequest(BaseModel):
    name: str
    rule_type: str  # phone, email, id_card, bank_card, name, address, custom
    pattern: str | None = None
    replacement: str | None = None
    description: str | None = None
    priority: int = 0


class MaskingRuleResponse(BaseModel):
    id: str
    project_id: str
    name: str
    rule_type: str
    pattern: str
    replacement: str
    is_active: bool
    priority: int
    description: str | None = None
    created_at: str
    updated_at: str


class MaskingApplyRequest(BaseModel):
    text: str
    rule_types: list[str] | None = None


class MaskingApplyResponse(BaseModel):
    original_text: str
    masked_text: str
    applied_rules: list[str]


class AccessPolicyCreateRequest(BaseModel):
    name: str
    description: str | None = None
    allowed_users: list[str] | None = None
    allowed_roles: list[str] | None = None
    allowed_ips: list[str] | None = None
    time_restrictions: dict | None = None
    max_access_count: int | None = None
    require_approval: bool = False


class AccessPolicyResponse(BaseModel):
    id: str
    project_id: str
    name: str
    description: str | None = None
    allowed_users: list[str] | None = None
    allowed_roles: list[str] | None = None
    allowed_ips: list[str] | None = None
    time_restrictions: dict | None = None
    max_access_count: int | None = None
    require_approval: bool = False
    is_active: bool = True
    created_at: str
    updated_at: str


class AccessRequestCreateRequest(BaseModel):
    policy_id: str
    request_reason: str | None = None
    expires_hours: int = 24


class AccessRequestResponse(BaseModel):
    id: str
    policy_id: str
    user_id: str
    request_reason: str | None = None
    status: str
    approved_by: str | None = None
    approved_at: str | None = None
    expires_at: str | None = None
    created_at: str


# ==================== Audit Logs API ====================


@app.get("/api/v1/audit-logs", response_model=list[AuditLogResponse], tags=["Security"])
async def get_audit_logs(
    user_id: str | None = None,
    resource_type: str | None = None,
    resource_id: str | None = None,
    action_type: str | None = None,
    start_time: str | None = None,
    end_time: str | None = None,
    success: bool | None = None,
    limit: int = 100,
    offset: int = 0,
    api_key: str = Depends(verify_api_key),
):
    """查询审计日志"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    logs = manager.get_audit_logs(
        user_id=user_id,
        resource_type=resource_type,
        resource_id=resource_id,
        action_type=action_type,
        start_time=start_time,
        end_time=end_time,
        success=success,
        limit=limit,
        offset=offset,
    )

    return [
        AuditLogResponse(
            id=log.id,
            action_type=log.action_type,
            user_id=log.user_id,
            user_ip=log.user_ip,
            resource_type=log.resource_type,
            resource_id=log.resource_id,
            action_details=log.action_details,
            success=log.success,
            error_message=log.error_message,
            created_at=log.created_at,
        )
        for log in logs
    ]


@app.get("/api/v1/audit-logs/stats", response_model=AuditStatsResponse, tags=["Security"])
async def get_audit_stats(
    start_time: str | None = None,
    end_time: str | None = None,
    api_key: str = Depends(verify_api_key),
):
    """获取审计统计"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    stats = manager.get_audit_stats(start_time=start_time, end_time=end_time)

    return AuditStatsResponse(**stats)


# ==================== Encryption API ====================


@app.post(
    "/api/v1/projects/{project_id}/encryption/enable",
    response_model=EncryptionConfigResponse,
    tags=["Security"],
)
async def enable_project_encryption(
    project_id: str, request: EncryptionEnableRequest, api_key: str = Depends(verify_api_key)
):
    """启用项目端到端加密"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    try:
        config = manager.enable_encryption(project_id, request.master_password)
        return EncryptionConfigResponse(
            id=config.id,
            project_id=config.project_id,
            is_enabled=config.is_enabled,
            encryption_type=config.encryption_type,
            created_at=config.created_at,
            updated_at=config.updated_at,
        )
    except RuntimeError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/projects/{project_id}/encryption/disable", tags=["Security"])
async def disable_project_encryption(
    project_id: str, request: EncryptionEnableRequest, api_key: str = Depends(verify_api_key)
):
    """禁用项目加密"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    success = manager.disable_encryption(project_id, request.master_password)

    if not success:
        raise HTTPException(status_code=400, detail="Invalid password or encryption not enabled")

    return {"success": True, "message": "Encryption disabled successfully"}


@app.post("/api/v1/projects/{project_id}/encryption/verify", tags=["Security"])
async def verify_encryption_password(
    project_id: str, request: EncryptionEnableRequest, api_key: str = Depends(verify_api_key)
):
    """验证加密密码"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    is_valid = manager.verify_encryption_password(project_id, request.master_password)

    return {"valid": is_valid}


@app.get(
    "/api/v1/projects/{project_id}/encryption",
    response_model=Optional[EncryptionConfigResponse],
    tags=["Security"],
)
async def get_encryption_config(project_id: str, api_key: str = Depends(verify_api_key)):
    """获取项目加密配置"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    config = manager.get_encryption_config(project_id)

    if not config:
        return None

    return EncryptionConfigResponse(
        id=config.id,
        project_id=config.project_id,
        is_enabled=config.is_enabled,
        encryption_type=config.encryption_type,
        created_at=config.created_at,
        updated_at=config.updated_at,
    )


# ==================== Data Masking API ====================


@app.post(
    "/api/v1/projects/{project_id}/masking-rules",
    response_model=MaskingRuleResponse,
    tags=["Security"],
)
async def create_masking_rule(
    project_id: str, request: MaskingRuleCreateRequest, api_key: str = Depends(verify_api_key)
):
    """创建数据脱敏规则"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    try:
        rule_type = MaskingRuleType(request.rule_type)
    except ValueError:
        raise HTTPException(status_code=400, detail=f"Invalid rule type: {request.rule_type}")

    rule = manager.create_masking_rule(
        project_id=project_id,
        name=request.name,
        rule_type=rule_type,
        pattern=request.pattern,
        replacement=request.replacement,
        description=request.description,
        priority=request.priority,
    )

    return MaskingRuleResponse(
        id=rule.id,
        project_id=rule.project_id,
        name=rule.name,
        rule_type=rule.rule_type,
        pattern=rule.pattern,
        replacement=rule.replacement,
        is_active=rule.is_active,
        priority=rule.priority,
        description=rule.description,
        created_at=rule.created_at,
        updated_at=rule.updated_at,
    )


@app.get(
    "/api/v1/projects/{project_id}/masking-rules",
    response_model=list[MaskingRuleResponse],
    tags=["Security"],
)
async def get_masking_rules(
    project_id: str, active_only: bool = True, api_key: str = Depends(verify_api_key)
):
    """获取项目脱敏规则"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    rules = manager.get_masking_rules(project_id, active_only=active_only)

    return [
        MaskingRuleResponse(
            id=rule.id,
            project_id=rule.project_id,
            name=rule.name,
            rule_type=rule.rule_type,
            pattern=rule.pattern,
            replacement=rule.replacement,
            is_active=rule.is_active,
            priority=rule.priority,
            description=rule.description,
            created_at=rule.created_at,
            updated_at=rule.updated_at,
        )
        for rule in rules
    ]


@app.put("/api/v1/masking-rules/{rule_id}", response_model=MaskingRuleResponse, tags=["Security"])
async def update_masking_rule(
    rule_id: str,
    name: str | None = None,
    pattern: str | None = None,
    replacement: str | None = None,
    is_active: bool | None = None,
    priority: int | None = None,
    description: str | None = None,
    api_key: str = Depends(verify_api_key),
):
    """更新脱敏规则"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    kwargs = {}
    if name is not None:
        kwargs["name"] = name
    if pattern is not None:
        kwargs["pattern"] = pattern
    if replacement is not None:
        kwargs["replacement"] = replacement
    if is_active is not None:
        kwargs["is_active"] = is_active
    if priority is not None:
        kwargs["priority"] = priority
    if description is not None:
        kwargs["description"] = description

    rule = manager.update_masking_rule(rule_id, **kwargs)

    if not rule:
        raise HTTPException(status_code=404, detail="Masking rule not found")

    return MaskingRuleResponse(
        id=rule.id,
        project_id=rule.project_id,
        name=rule.name,
        rule_type=rule.rule_type,
        pattern=rule.pattern,
        replacement=rule.replacement,
        is_active=rule.is_active,
        priority=rule.priority,
        description=rule.description,
        created_at=rule.created_at,
        updated_at=rule.updated_at,
    )


@app.delete("/api/v1/masking-rules/{rule_id}", tags=["Security"])
async def delete_masking_rule(rule_id: str, api_key: str = Depends(verify_api_key)):
    """删除脱敏规则"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    success = manager.delete_masking_rule(rule_id)

    if not success:
        raise HTTPException(status_code=404, detail="Masking rule not found")

    return {"success": True, "message": "Masking rule deleted"}


@app.post(
    "/api/v1/projects/{project_id}/masking/apply",
    response_model=MaskingApplyResponse,
    tags=["Security"],
)
async def apply_masking(
    project_id: str, request: MaskingApplyRequest, api_key: str = Depends(verify_api_key)
):
    """应用脱敏规则到文本"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    # 转换规则类型
    rule_types = None
    if request.rule_types:
        rule_types = [MaskingRuleType(rt) for rt in request.rule_types]

    masked_text = manager.apply_masking(request.text, project_id, rule_types)

    # 获取应用的规则
    rules = manager.get_masking_rules(project_id)
    applied_rules = [r.name for r in rules if r.is_active]

    return MaskingApplyResponse(
        original_text=request.text, masked_text=masked_text, applied_rules=applied_rules
    )


# ==================== Data Access Policy API ====================


@app.post(
    "/api/v1/projects/{project_id}/access-policies",
    response_model=AccessPolicyResponse,
    tags=["Security"],
)
async def create_access_policy(
    project_id: str, request: AccessPolicyCreateRequest, api_key: str = Depends(verify_api_key)
):
    """创建数据访问策略"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    policy = manager.create_access_policy(
        project_id=project_id,
        name=request.name,
        description=request.description,
        allowed_users=request.allowed_users,
        allowed_roles=request.allowed_roles,
        allowed_ips=request.allowed_ips,
        time_restrictions=request.time_restrictions,
        max_access_count=request.max_access_count,
        require_approval=request.require_approval,
    )

    return AccessPolicyResponse(
        id=policy.id,
        project_id=policy.project_id,
        name=policy.name,
        description=policy.description,
        allowed_users=json.loads(policy.allowed_users) if policy.allowed_users else None,
        allowed_roles=json.loads(policy.allowed_roles) if policy.allowed_roles else None,
        allowed_ips=json.loads(policy.allowed_ips) if policy.allowed_ips else None,
        time_restrictions=json.loads(policy.time_restrictions)
        if policy.time_restrictions
        else None,
        max_access_count=policy.max_access_count,
        require_approval=policy.require_approval,
        is_active=policy.is_active,
        created_at=policy.created_at,
        updated_at=policy.updated_at,
    )


@app.get(
    "/api/v1/projects/{project_id}/access-policies",
    response_model=list[AccessPolicyResponse],
    tags=["Security"],
)
async def get_access_policies(
    project_id: str, active_only: bool = True, api_key: str = Depends(verify_api_key)
):
    """获取项目访问策略"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    policies = manager.get_access_policies(project_id, active_only=active_only)

    return [
        AccessPolicyResponse(
            id=policy.id,
            project_id=policy.project_id,
            name=policy.name,
            description=policy.description,
            allowed_users=json.loads(policy.allowed_users) if policy.allowed_users else None,
            allowed_roles=json.loads(policy.allowed_roles) if policy.allowed_roles else None,
            allowed_ips=json.loads(policy.allowed_ips) if policy.allowed_ips else None,
            time_restrictions=json.loads(policy.time_restrictions)
            if policy.time_restrictions
            else None,
            max_access_count=policy.max_access_count,
            require_approval=policy.require_approval,
            is_active=policy.is_active,
            created_at=policy.created_at,
            updated_at=policy.updated_at,
        )
        for policy in policies
    ]


@app.post("/api/v1/access-policies/{policy_id}/check", tags=["Security"])
async def check_access_permission(
    policy_id: str, user_id: str, user_ip: str | None = None, api_key: str = Depends(verify_api_key)
):
    """检查访问权限"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    allowed, reason = manager.check_access_permission(policy_id, user_id, user_ip)

    return {"allowed": allowed, "reason": reason if not allowed else None}


# ==================== Access Request API ====================


@app.post("/api/v1/access-requests", response_model=AccessRequestResponse, tags=["Security"])
async def create_access_request(
    request: AccessRequestCreateRequest,
    user_id: str,  # 实际应该从认证信息中获取
    api_key: str = Depends(verify_api_key),
):
    """创建访问请求"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()

    access_request = manager.create_access_request(
        policy_id=request.policy_id,
        user_id=user_id,
        request_reason=request.request_reason,
        expires_hours=request.expires_hours,
    )

    return AccessRequestResponse(
        id=access_request.id,
        policy_id=access_request.policy_id,
        user_id=access_request.user_id,
        request_reason=access_request.request_reason,
        status=access_request.status,
        approved_by=access_request.approved_by,
        approved_at=access_request.approved_at,
        expires_at=access_request.expires_at,
        created_at=access_request.created_at,
    )


@app.post(
    "/api/v1/access-requests/{request_id}/approve",
    response_model=AccessRequestResponse,
    tags=["Security"],
)
async def approve_access_request(
    request_id: str,
    approved_by: str,
    expires_hours: int = 24,
    api_key: str = Depends(verify_api_key),
):
    """批准访问请求"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    access_request = manager.approve_access_request(request_id, approved_by, expires_hours)

    if not access_request:
        raise HTTPException(status_code=404, detail="Access request not found")

    return AccessRequestResponse(
        id=access_request.id,
        policy_id=access_request.policy_id,
        user_id=access_request.user_id,
        request_reason=access_request.request_reason,
        status=access_request.status,
        approved_by=access_request.approved_by,
        approved_at=access_request.approved_at,
        expires_at=access_request.expires_at,
        created_at=access_request.created_at,
    )


@app.post(
    "/api/v1/access-requests/{request_id}/reject",
    response_model=AccessRequestResponse,
    tags=["Security"],
)
async def reject_access_request(
    request_id: str, rejected_by: str, api_key: str = Depends(verify_api_key)
):
    """拒绝访问请求"""
    if not SECURITY_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Security manager not available")

    manager = get_security_manager()
    access_request = manager.reject_access_request(request_id, rejected_by)

    if not access_request:
        raise HTTPException(status_code=404, detail="Access request not found")

    return AccessRequestResponse(
        id=access_request.id,
        policy_id=access_request.policy_id,
        user_id=access_request.user_id,
        request_reason=access_request.request_reason,
        status=access_request.status,
        approved_by=access_request.approved_by,
        approved_at=access_request.approved_at,
        expires_at=access_request.expires_at,
        created_at=access_request.created_at,
    )


# ==========================================
# Phase 7 Task 4: 协作与共享 API
# ==========================================

# ----- 请求模型 -----


class ShareLinkCreate(BaseModel):
    permission: str = "read_only"  # read_only, comment, edit, admin
    expires_in_days: int | None = None
    max_uses: int | None = None
    password: str | None = None
    allow_download: bool = False
    allow_export: bool = False


class ShareLinkVerify(BaseModel):
    token: str
    password: str | None = None


class CommentCreate(BaseModel):
    target_type: str  # entity, relation, transcript, project
    target_id: str
    parent_id: str | None = None
    content: str
    mentions: list[str] | None = None


class CommentUpdate(BaseModel):
    content: str


class CommentResolve(BaseModel):
    resolved: bool


class TeamMemberInvite(BaseModel):
    user_id: str
    user_name: str
    user_email: str
    role: str = "viewer"  # owner, admin, editor, viewer, commenter


class TeamMemberRoleUpdate(BaseModel):
    role: str


# ----- 项目分享 -----


@app.post("/api/v1/projects/{project_id}/shares")
async def create_share_link(
    project_id: str, request: ShareLinkCreate, created_by: str = "current_user"
):
    """创建项目分享链接"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    share = manager.create_share_link(
        project_id=project_id,
        created_by=created_by,
        permission=request.permission,
        expires_in_days=request.expires_in_days,
        max_uses=request.max_uses,
        password=request.password,
        allow_download=request.allow_download,
        allow_export=request.allow_export,
    )

    return {
        "id": share.id,
        "token": share.token,
        "permission": share.permission,
        "created_at": share.created_at,
        "expires_at": share.expires_at,
        "max_uses": share.max_uses,
        "share_url": f"/share/{share.token}",
    }


@app.get("/api/v1/projects/{project_id}/shares")
async def list_project_shares(project_id: str):
    """列出项目的所有分享链接"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    shares = manager.list_project_shares(project_id)

    return {
        "shares": [
            {
                "id": s.id,
                "token": s.token,
                "permission": s.permission,
                "created_at": s.created_at,
                "expires_at": s.expires_at,
                "use_count": s.use_count,
                "max_uses": s.max_uses,
                "is_active": s.is_active,
                "has_password": s.password_hash is not None,
                "allow_download": s.allow_download,
                "allow_export": s.allow_export,
            }
            for s in shares
        ]
    }


@app.post("/api/v1/shares/verify")
async def verify_share_link(request: ShareLinkVerify):
    """验证分享链接"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    share = manager.validate_share_token(request.token, request.password)

    if not share:
        raise HTTPException(status_code=401, detail="Invalid or expired share link")

    # 增加使用次数
    manager.increment_share_usage(request.token)

    return {
        "valid": True,
        "project_id": share.project_id,
        "permission": share.permission,
        "allow_download": share.allow_download,
        "allow_export": share.allow_export,
    }


@app.get("/api/v1/shares/{token}/access")
async def access_shared_project(token: str, password: str | None = None):
    """通过分享链接访问项目"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    share = manager.validate_share_token(token, password)

    if not share:
        raise HTTPException(status_code=401, detail="Invalid or expired share link")

    # 增加使用次数
    manager.increment_share_usage(token)

    # 获取项目信息
    if not DB_AVAILABLE:
        raise HTTPException(status_code=503, detail="Database not available")

    db = get_db_manager()
    project = db.get_project(share.project_id)

    if not project:
        raise HTTPException(status_code=404, detail="Project not found")

    return {
        "project": {
            "id": project.id,
            "name": project.name,
            "description": project.description,
            "created_at": project.created_at,
        },
        "permission": share.permission,
        "allow_download": share.allow_download,
        "allow_export": share.allow_export,
    }


@app.delete("/api/v1/shares/{share_id}")
async def revoke_share_link(share_id: str, revoked_by: str = "current_user"):
    """撤销分享链接"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.revoke_share_link(share_id, revoked_by)

    if not success:
        raise HTTPException(status_code=404, detail="Share link not found")

    return {"success": True, "message": "Share link revoked"}


# ----- 评论和批注 -----


@app.post("/api/v1/projects/{project_id}/comments")
async def add_comment(
    project_id: str, request: CommentCreate, author: str = "current_user", author_name: str = "User"
):
    """添加评论"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    comment = manager.add_comment(
        project_id=project_id,
        target_type=request.target_type,
        target_id=request.target_id,
        author=author,
        author_name=author_name,
        content=request.content,
        parent_id=request.parent_id,
        mentions=request.mentions,
    )

    return {
        "id": comment.id,
        "target_type": comment.target_type,
        "target_id": comment.target_id,
        "parent_id": comment.parent_id,
        "author": comment.author,
        "author_name": comment.author_name,
        "content": comment.content,
        "created_at": comment.created_at,
        "resolved": comment.resolved,
    }


@app.get("/api/v1/{target_type}/{target_id}/comments")
async def get_comments(target_type: str, target_id: str, include_resolved: bool = True):
    """获取评论列表"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    comments = manager.get_comments(target_type, target_id, include_resolved)

    return {
        "count": len(comments),
        "comments": [
            {
                "id": c.id,
                "parent_id": c.parent_id,
                "author": c.author,
                "author_name": c.author_name,
                "content": c.content,
                "created_at": c.created_at,
                "updated_at": c.updated_at,
                "resolved": c.resolved,
                "resolved_by": c.resolved_by,
                "resolved_at": c.resolved_at,
            }
            for c in comments
        ],
    }


@app.get("/api/v1/projects/{project_id}/comments")
async def get_project_comments(project_id: str, limit: int = 50, offset: int = 0):
    """获取项目下的所有评论"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    comments = manager.get_project_comments(project_id, limit, offset)

    return {
        "count": len(comments),
        "comments": [
            {
                "id": c.id,
                "target_type": c.target_type,
                "target_id": c.target_id,
                "parent_id": c.parent_id,
                "author": c.author,
                "author_name": c.author_name,
                "content": c.content,
                "created_at": c.created_at,
                "resolved": c.resolved,
            }
            for c in comments
        ],
    }


@app.put("/api/v1/comments/{comment_id}")
async def update_comment(comment_id: str, request: CommentUpdate, updated_by: str = "current_user"):
    """更新评论"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    comment = manager.update_comment(comment_id, request.content, updated_by)

    if not comment:
        raise HTTPException(status_code=404, detail="Comment not found or not authorized")

    return {"id": comment.id, "content": comment.content, "updated_at": comment.updated_at}


@app.post("/api/v1/comments/{comment_id}/resolve")
async def resolve_comment(comment_id: str, resolved_by: str = "current_user"):
    """标记评论为已解决"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.resolve_comment(comment_id, resolved_by)

    if not success:
        raise HTTPException(status_code=404, detail="Comment not found")

    return {"success": True, "message": "Comment resolved"}


@app.delete("/api/v1/comments/{comment_id}")
async def delete_comment(comment_id: str, deleted_by: str = "current_user"):
    """删除评论"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.delete_comment(comment_id, deleted_by)

    if not success:
        raise HTTPException(status_code=404, detail="Comment not found or not authorized")

    return {"success": True, "message": "Comment deleted"}


# ----- 变更历史 -----


@app.get("/api/v1/projects/{project_id}/history")
async def get_change_history(
    project_id: str,
    entity_type: str | None = None,
    entity_id: str | None = None,
    limit: int = 50,
    offset: int = 0,
):
    """获取变更历史"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    records = manager.get_change_history(project_id, entity_type, entity_id, limit, offset)

    return {
        "count": len(records),
        "history": [
            {
                "id": r.id,
                "change_type": r.change_type,
                "entity_type": r.entity_type,
                "entity_id": r.entity_id,
                "entity_name": r.entity_name,
                "changed_by": r.changed_by,
                "changed_by_name": r.changed_by_name,
                "changed_at": r.changed_at,
                "old_value": r.old_value,
                "new_value": r.new_value,
                "description": r.description,
                "reverted": r.reverted,
            }
            for r in records
        ],
    }


@app.get("/api/v1/projects/{project_id}/history/stats")
async def get_change_history_stats(project_id: str):
    """获取变更统计"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    stats = manager.get_change_stats(project_id)

    return stats


@app.get("/api/v1/{entity_type}/{entity_id}/versions")
async def get_entity_versions(entity_type: str, entity_id: str):
    """获取实体版本历史"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    records = manager.get_entity_version_history(entity_type, entity_id)

    return {
        "count": len(records),
        "versions": [
            {
                "id": r.id,
                "change_type": r.change_type,
                "changed_by": r.changed_by,
                "changed_by_name": r.changed_by_name,
                "changed_at": r.changed_at,
                "old_value": r.old_value,
                "new_value": r.new_value,
                "description": r.description,
            }
            for r in records
        ],
    }


@app.post("/api/v1/history/{record_id}/revert")
async def revert_change(record_id: str, reverted_by: str = "current_user"):
    """回滚变更"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.revert_change(record_id, reverted_by)

    if not success:
        raise HTTPException(status_code=404, detail="Change record not found or already reverted")

    return {"success": True, "message": "Change reverted"}


# ----- 团队成员 -----


@app.post("/api/v1/projects/{project_id}/members")
async def invite_team_member(
    project_id: str, request: TeamMemberInvite, invited_by: str = "current_user"
):
    """邀请团队成员"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    member = manager.add_team_member(
        project_id=project_id,
        user_id=request.user_id,
        user_name=request.user_name,
        user_email=request.user_email,
        role=request.role,
        invited_by=invited_by,
    )

    return {
        "id": member.id,
        "user_id": member.user_id,
        "user_name": member.user_name,
        "user_email": member.user_email,
        "role": member.role,
        "joined_at": member.joined_at,
        "permissions": member.permissions,
    }


@app.get("/api/v1/projects/{project_id}/members")
async def list_team_members(project_id: str):
    """列出团队成员"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    members = manager.get_team_members(project_id)

    return {
        "count": len(members),
        "members": [
            {
                "id": m.id,
                "user_id": m.user_id,
                "user_name": m.user_name,
                "user_email": m.user_email,
                "role": m.role,
                "joined_at": m.joined_at,
                "last_active_at": m.last_active_at,
                "permissions": m.permissions,
            }
            for m in members
        ],
    }


@app.put("/api/v1/members/{member_id}/role")
async def update_member_role(
    member_id: str, request: TeamMemberRoleUpdate, updated_by: str = "current_user"
):
    """更新成员角色"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.update_member_role(member_id, request.role, updated_by)

    if not success:
        raise HTTPException(status_code=404, detail="Member not found")

    return {"success": True, "message": "Member role updated"}


@app.delete("/api/v1/members/{member_id}")
async def remove_team_member(member_id: str, removed_by: str = "current_user"):
    """移除团队成员"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    success = manager.remove_team_member(member_id, removed_by)

    if not success:
        raise HTTPException(status_code=404, detail="Member not found")

    return {"success": True, "message": "Member removed"}


@app.get("/api/v1/projects/{project_id}/permissions")
async def check_project_permissions(project_id: str, user_id: str = "current_user"):
    """检查用户权限"""
    if not COLLABORATION_AVAILABLE:
        raise HTTPException(status_code=503, detail="Collaboration module not available")

    manager = get_collab_manager()
    members = manager.get_team_members(project_id)

    user_member = None
    for m in members:
        if m.user_id == user_id:
            user_member = m
            break

    if not user_member:
        return {"has_access": False, "role": None, "permissions": []}

    return {"has_access": True, "role": user_member.role, "permissions": user_member.permissions}


# ==================== Phase 7 Task 6: Advanced Search & Discovery ====================


class FullTextSearchRequest(BaseModel):
    """全文搜索请求"""

    query: str
    content_types: list[str] | None = None
    operator: str = "AND"  # AND, OR, NOT
    limit: int = 20


class SemanticSearchRequest(BaseModel):
    """语义搜索请求"""

    query: str
    content_types: list[str] | None = None
    threshold: float = 0.7
    limit: int = 20


@app.post("/api/v1/search/fulltext", tags=["Search"])
async def fulltext_search(
    project_id: str, request: FullTextSearchRequest, _=Depends(verify_api_key)
):
    """全文搜索"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()

    try:
        operator = SearchOperator(request.operator.upper())
    except ValueError:
        operator = SearchOperator.AND

    results = search_manager.fulltext_search.search(
        query=request.query,
        project_id=project_id,
        content_types=request.content_types,
        operator=operator,
        limit=request.limit,
    )

    return {
        "query": request.query,
        "operator": request.operator,
        "total": len(results),
        "results": [
            {
                "id": r.id,
                "type": r.type,
                "title": r.title,
                "content": r.content,
                "highlights": r.highlights,
                "score": r.score,
            }
            for r in results
        ],
    }


@app.post("/api/v1/search/semantic", tags=["Search"])
async def semantic_search(
    project_id: str, request: SemanticSearchRequest, _=Depends(verify_api_key)
):
    """语义搜索"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()

    results = search_manager.semantic_search.search(
        query=request.query,
        project_id=project_id,
        content_types=request.content_types,
        threshold=request.threshold,
        limit=request.limit,
    )

    return {
        "query": request.query,
        "threshold": request.threshold,
        "total": len(results),
        "results": [
            {"id": r.id, "type": r.type, "text": r.text, "similarity": r.similarity}
            for r in results
        ],
    }


@app.get("/api/v1/entities/{entity_id}/paths/{target_entity_id}", tags=["Search"])
async def find_entity_paths(
    entity_id: str,
    target_entity_id: str,
    max_depth: int = 5,
    find_all: bool = False,
    _=Depends(verify_api_key),
):
    """查找实体关系路径"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()

    if find_all:
        paths = search_manager.path_discovery.find_all_paths(
            source_entity_id=entity_id, target_entity_id=target_entity_id, max_depth=max_depth
        )
    else:
        path = search_manager.path_discovery.find_shortest_path(
            source_entity_id=entity_id, target_entity_id=target_entity_id, max_depth=max_depth
        )
        paths = [path] if path else []

    return {
        "source_entity_id": entity_id,
        "target_entity_id": target_entity_id,
        "path_count": len(paths),
        "paths": [
            {
                "path_id": p.path_id,
                "path_length": p.path_length,
                "nodes": p.nodes,
                "edges": p.edges,
                "confidence": p.confidence,
            }
            for p in paths
        ],
    }


@app.get("/api/v1/entities/{entity_id}/network", tags=["Search"])
async def get_entity_network(entity_id: str, depth: int = 2, _=Depends(verify_api_key)):
    """获取实体关系网络"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()
    network = search_manager.path_discovery.get_entity_network(entity_id, depth)

    return network


@app.get("/api/v1/projects/{project_id}/knowledge-gaps", tags=["Search"])
async def detect_knowledge_gaps(project_id: str, _=Depends(verify_api_key)):
    """检测知识缺口"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()

    gaps = search_manager.gap_detector.detect_gaps(project_id)
    completeness = search_manager.gap_detector.get_completeness_score(project_id)

    return {
        "project_id": project_id,
        "completeness": completeness,
        "gap_count": len(gaps),
        "gaps": [
            {
                "gap_id": g.gap_id,
                "gap_type": g.gap_type,
                "entity_id": g.entity_id,
                "entity_name": g.entity_name,
                "description": g.description,
                "severity": g.severity,
                "suggestion": g.suggestion,
            }
            for g in gaps
        ],
    }


@app.post("/api/v1/projects/{project_id}/search/index", tags=["Search"])
async def index_project_for_search(project_id: str, _=Depends(verify_api_key)):
    """为项目创建搜索索引"""
    if not SEARCH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Search manager not available")

    search_manager = get_search_manager()
    success = search_manager.index_project_content(project_id)

    if success:
        return {"message": "Project indexed successfully", "project_id": project_id}
    else:
        raise HTTPException(status_code=500, detail="Failed to index project")


# ==================== Phase 7 Task 8: Performance & Scaling ====================


@app.get("/api/v1/cache/stats", tags=["Performance"])
async def get_cache_stats(_=Depends(verify_api_key)):
    """获取缓存统计"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    stats = perf_manager.cache.get_stats()

    return {
        "total_keys": stats.total_keys,
        "memory_usage_bytes": stats.memory_usage,
        "hit_count": stats.hit_count,
        "miss_count": stats.miss_count,
        "hit_rate": stats.hit_rate,
        "evicted_count": stats.evicted_count,
        "expired_count": stats.expired_count,
    }


@app.post("/api/v1/cache/clear", tags=["Performance"])
async def clear_cache(pattern: str | None = None, _=Depends(verify_api_key)):
    """清除缓存"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    success = perf_manager.cache.clear(pattern)

    if success:
        return {"message": "Cache cleared successfully", "pattern": pattern}
    else:
        raise HTTPException(status_code=500, detail="Failed to clear cache")


@app.get("/api/v1/performance/metrics", tags=["Performance"])
async def get_performance_metrics(
    metric_type: str | None = None,
    endpoint: str | None = None,
    hours: int = 24,
    limit: int = 1000,
    _=Depends(verify_api_key),
):
    """获取性能指标"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()

    start_time = (datetime.now() - timedelta(hours=hours)).isoformat()

    metrics = perf_manager.monitor.get_metrics(
        metric_type=metric_type, endpoint=endpoint, start_time=start_time, limit=limit
    )

    return {
        "period_hours": hours,
        "total": len(metrics),
        "metrics": [
            {
                "id": m.id,
                "metric_type": m.metric_type,
                "endpoint": m.endpoint,
                "duration_ms": m.duration_ms,
                "status_code": m.status_code,
                "timestamp": m.timestamp,
            }
            for m in metrics
        ],
    }


@app.get("/api/v1/performance/summary", tags=["Performance"])
async def get_performance_summary(hours: int = 24, _=Depends(verify_api_key)):
    """获取性能汇总统计"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    summary = perf_manager.monitor.get_summary_stats(hours)

    return summary


@app.get("/api/v1/tasks/{task_id}/status", tags=["Performance"])
async def get_task_status(task_id: str, _=Depends(verify_api_key)):
    """获取任务状态"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    task = perf_manager.task_queue.get_task_status(task_id)

    if not task:
        raise HTTPException(status_code=404, detail="Task not found")

    return {
        "task_id": task.task_id,
        "task_type": task.task_type,
        "status": task.status,
        "project_id": task.project_id,
        "params": task.params,
        "result": task.result,
        "error": task.error,
        "created_at": task.created_at,
        "started_at": task.started_at,
        "completed_at": task.completed_at,
        "retry_count": task.retry_count,
        "priority": task.priority,
    }


@app.get("/api/v1/tasks", tags=["Performance"])
async def list_tasks(
    project_id: str | None = None,
    status: str | None = None,
    limit: int = 50,
    _=Depends(verify_api_key),
):
    """列出任务"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    tasks = perf_manager.task_queue.list_tasks(project_id, status, limit)

    return {
        "total": len(tasks),
        "tasks": [
            {
                "task_id": t.task_id,
                "task_type": t.task_type,
                "status": t.status,
                "project_id": t.project_id,
                "created_at": t.created_at,
                "retry_count": t.retry_count,
                "priority": t.priority,
            }
            for t in tasks
        ],
    }


@app.post("/api/v1/tasks/{task_id}/cancel", tags=["Performance"])
async def cancel_task(task_id: str, _=Depends(verify_api_key)):
    """取消任务"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    success = perf_manager.task_queue.cancel_task(task_id)

    if success:
        return {"message": "Task cancelled successfully", "task_id": task_id}
    else:
        raise HTTPException(
            status_code=400, detail="Failed to cancel task or task already completed"
        )


@app.get("/api/v1/shards", tags=["Performance"])
async def list_shards(_=Depends(verify_api_key)):
    """列出数据库分片"""
    if not PERFORMANCE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Performance manager not available")

    perf_manager = get_performance_manager()
    shards = perf_manager.sharding.get_shard_stats()

    return {
        "shard_count": len(shards),
        "shards": [
            {
                "shard_id": s.shard_id,
                "entity_count": s.entity_count,
                "db_path": s.db_path,
                "created_at": s.created_at,
            }
            for s in shards
        ],
    }


# ============================================
# Phase 8: Multi-Tenant SaaS APIs
# ============================================


class CreateTenantRequest(BaseModel):
    name: str
    description: str | None = None
    tier: str = "free"


class UpdateTenantRequest(BaseModel):
    name: str | None = None
    description: str | None = None
    tier: str | None = None
    status: str | None = None


class AddDomainRequest(BaseModel):
    domain: str
    is_primary: bool = False


class UpdateBrandingRequest(BaseModel):
    logo_url: str | None = None
    favicon_url: str | None = None
    primary_color: str | None = None
    secondary_color: str | None = None
    custom_css: str | None = None
    custom_js: str | None = None
    login_page_bg: str | None = None


class InviteMemberRequest(BaseModel):
    email: str
    role: str = "member"


class UpdateMemberRequest(BaseModel):
    role: str | None = None


# Tenant Management APIs
@app.post("/api/v1/tenants", tags=["Tenants"])
async def create_tenant(
    request: CreateTenantRequest,
    user_id: str = Header(..., description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """创建新租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    try:
        tenant = manager.create_tenant(
            name=request.name, owner_id=user_id, tier=request.tier, description=request.description
        )
        return {
            "id": tenant.id,
            "name": tenant.name,
            "slug": tenant.slug,
            "tier": tenant.tier,
            "status": tenant.status,
            "created_at": tenant.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants", tags=["Tenants"])
async def list_my_tenants(
    user_id: str = Header(..., description="当前用户ID"), _=Depends(verify_api_key)
):
    """获取当前用户的所有租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    tenants = manager.get_user_tenants(user_id)
    return {"tenants": tenants}


@app.get("/api/v1/tenants/{tenant_id}", tags=["Tenants"])
async def get_tenant(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户详情"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    tenant = manager.get_tenant(tenant_id)

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return {
        "id": tenant.id,
        "name": tenant.name,
        "slug": tenant.slug,
        "description": tenant.description,
        "tier": tenant.tier,
        "status": tenant.status,
        "owner_id": tenant.owner_id,
        "created_at": tenant.created_at.isoformat(),
        "settings": tenant.settings,
        "resource_limits": tenant.resource_limits,
    }


@app.put("/api/v1/tenants/{tenant_id}", tags=["Tenants"])
async def update_tenant(tenant_id: str, request: UpdateTenantRequest, _=Depends(verify_api_key)):
    """更新租户信息"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    tenant = manager.update_tenant(
        tenant_id=tenant_id,
        name=request.name,
        description=request.description,
        tier=request.tier,
        status=request.status,
    )

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return {
        "id": tenant.id,
        "name": tenant.name,
        "slug": tenant.slug,
        "tier": tenant.tier,
        "status": tenant.status,
        "updated_at": tenant.updated_at.isoformat(),
    }


@app.delete("/api/v1/tenants/{tenant_id}", tags=["Tenants"])
async def delete_tenant(tenant_id: str, _=Depends(verify_api_key)):
    """删除租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    success = manager.delete_tenant(tenant_id)

    if not success:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return {"message": "Tenant deleted successfully"}


# Domain Management APIs
@app.post("/api/v1/tenants/{tenant_id}/domains", tags=["Tenants"])
async def add_domain(tenant_id: str, request: AddDomainRequest, _=Depends(verify_api_key)):
    """为租户添加自定义域名"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    try:
        domain = manager.add_domain(
            tenant_id=tenant_id, domain=request.domain, is_primary=request.is_primary
        )

        # 获取验证指导
        instructions = manager.get_domain_verification_instructions(domain.id)

        return {
            "id": domain.id,
            "domain": domain.domain,
            "status": domain.status,
            "is_primary": domain.is_primary,
            "verification_token": domain.verification_token,
            "verification_instructions": instructions,
            "created_at": domain.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/domains", tags=["Tenants"])
async def list_domains(tenant_id: str, _=Depends(verify_api_key)):
    """列出租户的所有域名"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    domains = manager.list_domains(tenant_id)

    return {
        "domains": [
            {
                "id": d.id,
                "domain": d.domain,
                "status": d.status,
                "is_primary": d.is_primary,
                "ssl_enabled": d.ssl_enabled,
                "verified_at": d.verified_at.isoformat() if d.verified_at else None,
                "created_at": d.created_at.isoformat(),
            }
            for d in domains
        ]
    }


@app.post("/api/v1/tenants/{tenant_id}/domains/{domain_id}/verify", tags=["Tenants"])
async def verify_domain(tenant_id: str, domain_id: str, _=Depends(verify_api_key)):
    """验证域名所有权"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    success = manager.verify_domain(tenant_id, domain_id)

    return {
        "success": success,
        "message": "Domain verified successfully" if success else "Domain verification failed",
    }


@app.delete("/api/v1/tenants/{tenant_id}/domains/{domain_id}", tags=["Tenants"])
async def remove_domain(tenant_id: str, domain_id: str, _=Depends(verify_api_key)):
    """移除域名绑定"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    success = manager.remove_domain(tenant_id, domain_id)

    if not success:
        raise HTTPException(status_code=404, detail="Domain not found")

    return {"message": "Domain removed successfully"}


# Branding APIs
@app.get("/api/v1/tenants/{tenant_id}/branding", tags=["Tenants"])
async def get_branding(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户品牌配置"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    branding = manager.get_branding(tenant_id)

    if not branding:
        return {
            "tenant_id": tenant_id,
            "logo_url": None,
            "favicon_url": None,
            "primary_color": None,
            "secondary_color": None,
            "custom_css": None,
        }

    return {
        "tenant_id": branding.tenant_id,
        "logo_url": branding.logo_url,
        "favicon_url": branding.favicon_url,
        "primary_color": branding.primary_color,
        "secondary_color": branding.secondary_color,
        "custom_css": branding.custom_css,
        "custom_js": branding.custom_js,
        "login_page_bg": branding.login_page_bg,
    }


@app.put("/api/v1/tenants/{tenant_id}/branding", tags=["Tenants"])
async def update_branding(
    tenant_id: str, request: UpdateBrandingRequest, _=Depends(verify_api_key)
):
    """更新租户品牌配置"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    branding = manager.update_branding(
        tenant_id=tenant_id,
        logo_url=request.logo_url,
        favicon_url=request.favicon_url,
        primary_color=request.primary_color,
        secondary_color=request.secondary_color,
        custom_css=request.custom_css,
        custom_js=request.custom_js,
        login_page_bg=request.login_page_bg,
    )

    return {
        "tenant_id": branding.tenant_id,
        "logo_url": branding.logo_url,
        "favicon_url": branding.favicon_url,
        "primary_color": branding.primary_color,
        "secondary_color": branding.secondary_color,
        "updated_at": branding.updated_at.isoformat(),
    }


@app.get("/api/v1/tenants/{tenant_id}/branding.css", tags=["Tenants"])
async def get_branding_css(tenant_id: str):
    """获取租户品牌 CSS（公开端点，无需认证）"""
    if not TENANT_MANAGER_AVAILABLE:
        return ""

    manager = get_tenant_manager()
    css = manager.get_branding_css(tenant_id)

    return PlainTextResponse(content=css, media_type="text/css")


# Member Management APIs
@app.post("/api/v1/tenants/{tenant_id}/members", tags=["Tenants"])
async def invite_member(
    tenant_id: str,
    request: InviteMemberRequest,
    user_id: str = Header(..., description="邀请者用户ID"),
    _=Depends(verify_api_key),
):
    """邀请成员加入租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    try:
        member = manager.invite_member(
            tenant_id=tenant_id, email=request.email, role=request.role, invited_by=user_id
        )

        return {
            "id": member.id,
            "email": member.email,
            "role": member.role,
            "status": member.status,
            "invited_at": member.invited_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/members", tags=["Tenants"])
async def list_members(tenant_id: str, status: str | None = None, _=Depends(verify_api_key)):
    """列出租户成员"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    members = manager.list_members(tenant_id, status)

    return {
        "members": [
            {
                "id": m.id,
                "user_id": m.user_id,
                "email": m.email,
                "role": m.role,
                "status": m.status,
                "permissions": m.permissions,
                "invited_at": m.invited_at.isoformat(),
                "joined_at": m.joined_at.isoformat() if m.joined_at else None,
                "last_active_at": m.last_active_at.isoformat() if m.last_active_at else None,
            }
            for m in members
        ]
    }


@app.put("/api/v1/tenants/{tenant_id}/members/{member_id}", tags=["Tenants"])
async def update_member(
    tenant_id: str, member_id: str, request: UpdateMemberRequest, _=Depends(verify_api_key)
):
    """更新成员角色"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    success = manager.update_member_role(tenant_id, member_id, request.role)

    if not success:
        raise HTTPException(status_code=404, detail="Member not found")

    return {"message": "Member updated successfully"}


@app.delete("/api/v1/tenants/{tenant_id}/members/{member_id}", tags=["Tenants"])
async def remove_member(tenant_id: str, member_id: str, _=Depends(verify_api_key)):
    """移除成员"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    success = manager.remove_member(tenant_id, member_id)

    if not success:
        raise HTTPException(status_code=404, detail="Member not found")

    return {"message": "Member removed successfully"}


# Usage & Limits APIs
@app.get("/api/v1/tenants/{tenant_id}/usage", tags=["Tenants"])
async def get_tenant_usage(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户资源使用统计"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    stats = manager.get_usage_stats(tenant_id)

    return stats


@app.get("/api/v1/tenants/{tenant_id}/limits/{resource_type}", tags=["Tenants"])
async def check_resource_limit(tenant_id: str, resource_type: str, _=Depends(verify_api_key)):
    """检查特定资源是否超限"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    allowed, current, limit = manager.check_resource_limit(tenant_id, resource_type)

    return {
        "resource_type": resource_type,
        "allowed": allowed,
        "current": current,
        "limit": limit,
        "usage_percentage": round(current / limit * 100, 2) if limit > 0 else 0,
    }


# Public tenant resolution API (for custom domains)
@app.get("/api/v1/resolve-tenant", tags=["Tenants"])
async def resolve_tenant_by_domain(domain: str):
    """通过域名解析租户（用于自定义域名路由）"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    manager = get_tenant_manager()
    tenant = manager.get_tenant_by_domain(domain)

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found for this domain")

    branding = manager.get_branding(tenant.id)

    return {
        "tenant_id": tenant.id,
        "name": tenant.name,
        "slug": tenant.slug,
        "tier": tenant.tier,
        "branding": {
            "logo_url": branding.logo_url if branding else None,
            "primary_color": branding.primary_color if branding else None,
            "favicon_url": branding.favicon_url if branding else None,
        },
    }


@app.get("/api/v1/health", tags=["System"])
async def detailed_health_check():
    """健康检查"""
    health = {"status": "healthy", "timestamp": datetime.now().isoformat(), "components": {}}

    # 数据库检查
    if DB_AVAILABLE:
        try:
            db = get_db_manager()
            conn = db.get_conn()
            conn.execute("SELECT 1")
            conn.close()
            health["components"]["database"] = "ok"
        except Exception as e:
            health["components"]["database"] = f"error: {str(e)}"
            health["status"] = "degraded"
    else:
        health["components"]["database"] = "unavailable"

    # 性能管理器检查
    if PERFORMANCE_MANAGER_AVAILABLE:
        try:
            perf_manager = get_performance_manager()
            perf_health = perf_manager.health_check()
            health["components"].update(perf_health)
            if perf_health.get("overall") != "healthy":
                health["status"] = "degraded"
        except Exception as e:
            health["components"]["performance"] = f"error: {str(e)}"
            health["status"] = "degraded"

    # 搜索管理器检查
    if SEARCH_MANAGER_AVAILABLE:
        health["components"]["search"] = "available"
    else:
        health["components"]["search"] = "unavailable"

    # 租户管理器检查
    if TENANT_MANAGER_AVAILABLE:
        health["components"]["tenant"] = "available"
    else:
        health["components"]["tenant"] = "unavailable"

    return health


# ==================== Phase 8: Multi-Tenant SaaS API ====================


# Pydantic Models for Tenant API
class TenantCreate(BaseModel):
    name: str = Field(..., description="租户名称")
    slug: str = Field(..., description="URL 友好的唯一标识（小写字母、数字、连字符）")
    description: str = Field(default="", description="租户描述")
    plan: str = Field(
        default="free", description="套餐类型: free, starter, professional, enterprise"
    )
    billing_email: str = Field(default="", description="计费邮箱")


class TenantUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    status: str | None = None
    plan: str | None = None
    billing_email: str | None = None
    max_projects: int | None = None
    max_members: int | None = None


class TenantResponse(BaseModel):
    id: str
    name: str
    slug: str
    description: str
    status: str
    plan: str
    max_projects: int
    max_members: int
    max_storage_gb: float
    max_api_calls_per_day: int
    billing_email: str
    created_at: str
    updated_at: str


class TenantDomainCreate(BaseModel):
    domain: str = Field(..., description="自定义域名")


class TenantDomainResponse(BaseModel):
    id: str
    tenant_id: str
    domain: str
    status: str
    verification_record: str
    verification_expires_at: str | None
    ssl_enabled: bool
    created_at: str
    verified_at: str | None


class TenantBrandingUpdate(BaseModel):
    logo_url: str | None = None
    logo_dark_url: str | None = None
    favicon_url: str | None = None
    primary_color: str | None = None
    secondary_color: str | None = None
    accent_color: str | None = None
    background_color: str | None = None
    text_color: str | None = None
    dark_primary_color: str | None = None
    dark_background_color: str | None = None
    dark_text_color: str | None = None
    font_family: str | None = None
    custom_css: str | None = None
    custom_js: str | None = None
    app_name: str | None = None
    login_page_title: str | None = None
    login_page_description: str | None = None
    footer_text: str | None = None


class TenantMemberInvite(BaseModel):
    email: str = Field(..., description="被邀请者邮箱")
    name: str = Field(default="", description="被邀请者姓名")
    role: str = Field(default="viewer", description="角色: owner, admin, editor, viewer, guest")


class TenantMemberResponse(BaseModel):
    id: str
    tenant_id: str
    user_id: str
    email: str
    name: str
    role: str
    status: str
    invited_by: str | None
    invited_at: str | None
    joined_at: str | None
    last_active_at: str | None
    created_at: str


class TenantRoleCreate(BaseModel):
    name: str = Field(..., description="角色名称")
    description: str = Field(default="", description="角色描述")
    permissions: list[str] = Field(default_factory=list, description="权限列表")


class TenantRoleResponse(BaseModel):
    id: str
    tenant_id: str
    name: str
    description: str
    permissions: list[str]
    is_system: bool
    created_at: str


class TenantStatsResponse(BaseModel):
    tenant_id: str
    project_count: int
    member_count: int
    storage_used_gb: float
    api_calls_today: int
    api_calls_month: int


# Tenant API Endpoints
@app.post("/api/v1/tenants", response_model=TenantResponse, tags=["Tenants"])
async def create_tenant_endpoint(tenant: TenantCreate, request: Request, _=Depends(verify_api_key)):
    """创建新租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 获取当前用户ID（从请求状态或API Key）
    user_id = ""
    if hasattr(request.state, "api_key") and request.state.api_key:
        user_id = request.state.api_key.created_by or ""

    try:
        new_tenant = tenant_manager.create_tenant(
            name=tenant.name,
            slug=tenant.slug,
            created_by=user_id,
            description=tenant.description,
            plan=TenantTier(tenant.plan),
            billing_email=tenant.billing_email,
        )
        return new_tenant.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants", response_model=list[TenantResponse], tags=["Tenants"])
async def list_tenants_endpoint(
    status: str | None = None,
    plan: str | None = None,
    limit: int = 100,
    offset: int = 0,
    _=Depends(verify_api_key),
):
    """列出租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    status_enum = TenantStatus(status) if status else None
    plan_enum = TenantTier(plan) if plan else None

    tenants = tenant_manager.list_tenants(
        status=status_enum, plan=plan_enum, limit=limit, offset=offset
    )
    return [t.to_dict() for t in tenants]


@app.get("/api/v1/tenants/{tenant_id}", response_model=TenantResponse, tags=["Tenants"])
async def get_tenant_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户详情"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    tenant = tenant_manager.get_tenant(tenant_id)

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return tenant.to_dict()


@app.get("/api/v1/tenants/slug/{slug}", response_model=TenantResponse, tags=["Tenants"])
async def get_tenant_by_slug_endpoint(slug: str, _=Depends(verify_api_key)):
    """根据 slug 获取租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    tenant = tenant_manager.get_tenant_by_slug(slug)

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return tenant.to_dict()


@app.put("/api/v1/tenants/{tenant_id}", response_model=TenantResponse, tags=["Tenants"])
async def update_tenant_endpoint(tenant_id: str, update: TenantUpdate, _=Depends(verify_api_key)):
    """更新租户信息"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 过滤掉 None 值
    update_data = {k: v for k, v in update.dict().items() if v is not None}

    try:
        updated = tenant_manager.update_tenant(tenant_id, **update_data)
        if not updated:
            raise HTTPException(status_code=404, detail="Tenant not found")
        return updated.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.delete("/api/v1/tenants/{tenant_id}", tags=["Tenants"])
async def delete_tenant_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """删除租户（标记为过期）"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    success = tenant_manager.delete_tenant(tenant_id)

    if not success:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return {"success": True, "message": f"Tenant {tenant_id} deleted"}


# Tenant Domain API
@app.post(
    "/api/v1/tenants/{tenant_id}/domains", response_model=TenantDomainResponse, tags=["Tenants"]
)
async def add_tenant_domain_endpoint(
    tenant_id: str, domain: TenantDomainCreate, _=Depends(verify_api_key)
):
    """为租户添加自定义域名"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 验证租户存在
    tenant = tenant_manager.get_tenant(tenant_id)
    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    try:
        new_domain = tenant_manager.add_domain(tenant_id, domain.domain)
        return new_domain.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get(
    "/api/v1/tenants/{tenant_id}/domains",
    response_model=list[TenantDomainResponse],
    tags=["Tenants"],
)
async def list_tenant_domains_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户的所有域名"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    domains = tenant_manager.get_tenant_domains(tenant_id)
    return [d.to_dict() for d in domains]


@app.post("/api/v1/tenants/{tenant_id}/domains/{domain_id}/verify", tags=["Tenants"])
async def verify_tenant_domain_endpoint(tenant_id: str, domain_id: str, _=Depends(verify_api_key)):
    """验证域名 DNS 记录"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    success = tenant_manager.verify_domain(tenant_id, domain_id)

    if not success:
        raise HTTPException(status_code=400, detail="Domain verification failed")

    return {"success": True, "message": "Domain verified successfully"}


@app.post("/api/v1/tenants/{tenant_id}/domains/{domain_id}/activate", tags=["Tenants"])
async def activate_tenant_domain_endpoint(
    tenant_id: str, domain_id: str, _=Depends(verify_api_key)
):
    """激活已验证的域名"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    success = tenant_manager.activate_domain(tenant_id, domain_id)

    if not success:
        raise HTTPException(status_code=400, detail="Domain activation failed")

    return {"success": True, "message": "Domain activated successfully"}


@app.delete("/api/v1/tenants/{tenant_id}/domains/{domain_id}", tags=["Tenants"])
async def remove_tenant_domain_endpoint(tenant_id: str, domain_id: str, _=Depends(verify_api_key)):
    """移除域名绑定"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    success = tenant_manager.remove_domain(tenant_id, domain_id)

    if not success:
        raise HTTPException(status_code=404, detail="Domain not found")

    return {"success": True, "message": "Domain removed successfully"}


# Tenant Branding API
@app.get("/api/v1/tenants/{tenant_id}/branding", tags=["Tenants"])
async def get_tenant_branding_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户品牌配置"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    branding = tenant_manager.get_branding(tenant_id)

    if not branding:
        raise HTTPException(status_code=404, detail="Branding not found")

    return branding.to_dict()


@app.put("/api/v1/tenants/{tenant_id}/branding", tags=["Tenants"])
async def update_tenant_branding_endpoint(
    tenant_id: str, branding: TenantBrandingUpdate, _=Depends(verify_api_key)
):
    """更新租户品牌配置"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 过滤掉 None 值
    update_data = {k: v for k, v in branding.dict().items() if v is not None}

    updated = tenant_manager.update_branding(tenant_id, **update_data)
    if not updated:
        raise HTTPException(status_code=404, detail="Branding not found")

    return updated.to_dict()


@app.get("/api/v1/tenants/{tenant_id}/branding/theme.css", tags=["Tenants"])
async def get_tenant_theme_css_endpoint(tenant_id: str):
    """获取租户主题 CSS（公开访问）"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    branding = tenant_manager.get_branding(tenant_id)

    if not branding:
        raise HTTPException(status_code=404, detail="Branding not found")

    return PlainTextResponse(content=branding.get_theme_css(), media_type="text/css")


# Tenant Member API
@app.post(
    "/api/v1/tenants/{tenant_id}/members/invite",
    response_model=TenantMemberResponse,
    tags=["Tenants"],
)
async def invite_tenant_member_endpoint(
    tenant_id: str, invite: TenantMemberInvite, request: Request, _=Depends(verify_api_key)
):
    """邀请成员加入租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 获取当前用户ID
    invited_by = ""
    if hasattr(request.state, "api_key") and request.state.api_key:
        invited_by = request.state.api_key.created_by or ""

    try:
        member = tenant_manager.invite_member(
            tenant_id=tenant_id,
            email=invite.email,
            role=TenantRole(invite.role),
            invited_by=invited_by,
            name=invite.name,
        )
        return member.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/tenants/members/accept-invitation", tags=["Tenants"])
async def accept_invitation_endpoint(token: str, user_id: str):
    """接受邀请加入租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    member = tenant_manager.accept_invitation(token, user_id)

    if not member:
        raise HTTPException(status_code=400, detail="Invalid or expired invitation token")

    return member.to_dict()


@app.get(
    "/api/v1/tenants/{tenant_id}/members",
    response_model=list[TenantMemberResponse],
    tags=["Tenants"],
)
async def list_tenant_members_endpoint(
    tenant_id: str, status: str | None = None, role: str | None = None, _=Depends(verify_api_key)
):
    """列出租户成员"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    status_enum = TenantStatus(status) if status else None
    role_enum = TenantRole(role) if role else None

    members = tenant_manager.list_members(tenant_id, status=status_enum, role=role_enum)
    return [m.to_dict() for m in members]


@app.put("/api/v1/tenants/{tenant_id}/members/{member_id}/role", tags=["Tenants"])
async def update_member_role_endpoint(
    tenant_id: str, member_id: str, role: str, request: Request, _=Depends(verify_api_key)
):
    """更新成员角色"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 获取当前用户ID
    updated_by = ""
    if hasattr(request.state, "api_key") and request.state.api_key:
        updated_by = request.state.api_key.created_by or ""

    try:
        updated = tenant_manager.update_member_role(
            tenant_id=tenant_id,
            member_id=member_id,
            new_role=TenantRole(role),
            updated_by=updated_by,
        )
        if not updated:
            raise HTTPException(status_code=404, detail="Member not found")
        return updated.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=403, detail=str(e))


@app.delete("/api/v1/tenants/{tenant_id}/members/{member_id}", tags=["Tenants"])
async def remove_tenant_member_endpoint(
    tenant_id: str, member_id: str, request: Request, _=Depends(verify_api_key)
):
    """移除租户成员"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    # 获取当前用户ID
    removed_by = ""
    if hasattr(request.state, "api_key") and request.state.api_key:
        removed_by = request.state.api_key.created_by or ""

    try:
        success = tenant_manager.remove_member(tenant_id, member_id, removed_by)
        if not success:
            raise HTTPException(status_code=404, detail="Member not found")
        return {"success": True, "message": "Member removed successfully"}
    except ValueError as e:
        raise HTTPException(status_code=403, detail=str(e))


# Tenant Role API
@app.get(
    "/api/v1/tenants/{tenant_id}/roles", response_model=list[TenantRoleResponse], tags=["Tenants"]
)
async def list_tenant_roles_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """列出租户角色"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    roles = tenant_manager.list_roles(tenant_id)
    return [r.to_dict() for r in roles]


@app.post("/api/v1/tenants/{tenant_id}/roles", response_model=TenantRoleResponse, tags=["Tenants"])
async def create_tenant_role_endpoint(
    tenant_id: str, role: TenantRoleCreate, _=Depends(verify_api_key)
):
    """创建自定义角色"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    try:
        new_role = tenant_manager.create_custom_role(
            tenant_id=tenant_id,
            name=role.name,
            description=role.description,
            permissions=role.permissions,
        )
        return new_role.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.put("/api/v1/tenants/{tenant_id}/roles/{role_id}/permissions", tags=["Tenants"])
async def update_role_permissions_endpoint(
    tenant_id: str, role_id: str, permissions: list[str], _=Depends(verify_api_key)
):
    """更新角色权限"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    try:
        updated = tenant_manager.update_role_permissions(tenant_id, role_id, permissions)
        if not updated:
            raise HTTPException(status_code=404, detail="Role not found")
        return updated.to_dict()
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.delete("/api/v1/tenants/{tenant_id}/roles/{role_id}", tags=["Tenants"])
async def delete_tenant_role_endpoint(tenant_id: str, role_id: str, _=Depends(verify_api_key)):
    """删除自定义角色"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()

    try:
        success = tenant_manager.delete_role(tenant_id, role_id)
        if not success:
            raise HTTPException(status_code=404, detail="Role not found")
        return {"success": True, "message": "Role deleted successfully"}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/permissions", tags=["Tenants"])
async def list_tenant_permissions_endpoint(_=Depends(verify_api_key)):
    """获取所有可用的租户权限列表"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    return {
        "permissions": [{"id": k, "name": v} for k, v in tenant_manager.PERMISSION_NAMES.items()]
    }


# Tenant Resolution API
@app.get("/api/v1/tenants/resolve", tags=["Tenants"])
async def resolve_tenant_endpoint(
    host: str | None = None,
    slug: str | None = None,
    tenant_id: str | None = None,
    _=Depends(verify_api_key),
):
    """从请求信息解析租户"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    tenant = tenant_manager.resolve_tenant_from_request(host=host, slug=slug, tenant_id=tenant_id)

    if not tenant:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return tenant.to_dict()


@app.get("/api/v1/tenants/{tenant_id}/context", tags=["Tenants"])
async def get_tenant_context_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户完整上下文"""
    if not TENANT_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Tenant manager not available")

    tenant_manager = get_tenant_manager()
    context = tenant_manager.get_tenant_context(tenant_id)

    if not context:
        raise HTTPException(status_code=404, detail="Tenant not found")

    return context


# ============================================
# Phase 8 Task 2: Subscription & Billing APIs
# ============================================


# Pydantic Models for Subscription API
class CreateSubscriptionRequest(BaseModel):
    plan_id: str = Field(..., description="订阅计划ID")
    billing_cycle: str = Field(default="monthly", description="计费周期: monthly/yearly")
    payment_provider: str | None = Field(
        default=None, description="支付提供商: stripe/alipay/wechat"
    )
    trial_days: int = Field(default=0, description="试用天数")


class ChangePlanRequest(BaseModel):
    new_plan_id: str = Field(..., description="新计划ID")
    prorate: bool = Field(default=True, description="是否按比例计算差价")


class CancelSubscriptionRequest(BaseModel):
    at_period_end: bool = Field(default=True, description="是否在周期结束时取消")


class CreatePaymentRequest(BaseModel):
    amount: float = Field(..., description="支付金额")
    currency: str = Field(default="CNY", description="货币")
    provider: str = Field(..., description="支付提供商: stripe/alipay/wechat")
    payment_method: str | None = Field(default=None, description="支付方式")


class RequestRefundRequest(BaseModel):
    payment_id: str = Field(..., description="支付记录ID")
    amount: float = Field(..., description="退款金额")
    reason: str = Field(..., description="退款原因")


class ProcessRefundRequest(BaseModel):
    action: str = Field(..., description="操作: approve/reject")
    reason: str | None = Field(default=None, description="拒绝原因（拒绝时必填）")


class RecordUsageRequest(BaseModel):
    resource_type: str = Field(..., description="资源类型: transcription/storage/api_call/export")
    quantity: float = Field(..., description="使用量")
    unit: str = Field(..., description="单位: minutes/mb/count/page")
    description: str | None = Field(default=None, description="描述")


class CreateCheckoutSessionRequest(BaseModel):
    plan_id: str = Field(..., description="计划ID")
    billing_cycle: str = Field(default="monthly", description="计费周期")
    success_url: str = Field(..., description="支付成功回调URL")
    cancel_url: str = Field(..., description="支付取消回调URL")


# Subscription Plan APIs
@app.get("/api/v1/subscription-plans", tags=["Subscriptions"])
async def list_subscription_plans(
    include_inactive: bool = Query(default=False, description="包含已停用计划"),
    _=Depends(verify_api_key),
):
    """获取所有订阅计划"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    plans = manager.list_plans(include_inactive=include_inactive)

    return {
        "plans": [
            {
                "id": p.id,
                "name": p.name,
                "tier": p.tier,
                "description": p.description,
                "price_monthly": p.price_monthly,
                "price_yearly": p.price_yearly,
                "currency": p.currency,
                "features": p.features,
                "limits": p.limits,
                "is_active": p.is_active,
            }
            for p in plans
        ]
    }


@app.get("/api/v1/subscription-plans/{plan_id}", tags=["Subscriptions"])
async def get_subscription_plan(plan_id: str, _=Depends(verify_api_key)):
    """获取订阅计划详情"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    plan = manager.get_plan(plan_id)

    if not plan:
        raise HTTPException(status_code=404, detail="Plan not found")

    return {
        "id": plan.id,
        "name": plan.name,
        "tier": plan.tier,
        "description": plan.description,
        "price_monthly": plan.price_monthly,
        "price_yearly": plan.price_yearly,
        "currency": plan.currency,
        "features": plan.features,
        "limits": plan.limits,
        "is_active": plan.is_active,
        "created_at": plan.created_at.isoformat(),
    }


# Subscription APIs
@app.post("/api/v1/tenants/{tenant_id}/subscription", tags=["Subscriptions"])
async def create_subscription(
    tenant_id: str,
    request: CreateSubscriptionRequest,
    user_id: str = Header(..., description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """创建新订阅"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    try:
        subscription = manager.create_subscription(
            tenant_id=tenant_id,
            plan_id=request.plan_id,
            payment_provider=request.payment_provider,
            trial_days=request.trial_days,
            billing_cycle=request.billing_cycle,
        )

        return {
            "id": subscription.id,
            "tenant_id": subscription.tenant_id,
            "plan_id": subscription.plan_id,
            "status": subscription.status,
            "current_period_start": subscription.current_period_start.isoformat(),
            "current_period_end": subscription.current_period_end.isoformat(),
            "trial_start": subscription.trial_start.isoformat()
            if subscription.trial_start
            else None,
            "trial_end": subscription.trial_end.isoformat() if subscription.trial_end else None,
            "created_at": subscription.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/subscription", tags=["Subscriptions"])
async def get_tenant_subscription(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户当前订阅"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    subscription = manager.get_tenant_subscription(tenant_id)

    if not subscription:
        return {"subscription": None}

    plan = manager.get_plan(subscription.plan_id)

    return {
        "subscription": {
            "id": subscription.id,
            "tenant_id": subscription.tenant_id,
            "plan_id": subscription.plan_id,
            "plan_name": plan.name if plan else None,
            "plan_tier": plan.tier if plan else None,
            "status": subscription.status,
            "current_period_start": subscription.current_period_start.isoformat(),
            "current_period_end": subscription.current_period_end.isoformat(),
            "cancel_at_period_end": subscription.cancel_at_period_end,
            "canceled_at": subscription.canceled_at.isoformat()
            if subscription.canceled_at
            else None,
            "trial_start": subscription.trial_start.isoformat()
            if subscription.trial_start
            else None,
            "trial_end": subscription.trial_end.isoformat() if subscription.trial_end else None,
            "created_at": subscription.created_at.isoformat(),
        }
    }


@app.put("/api/v1/tenants/{tenant_id}/subscription/change-plan", tags=["Subscriptions"])
async def change_subscription_plan(
    tenant_id: str, request: ChangePlanRequest, _=Depends(verify_api_key)
):
    """更改订阅计划"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    subscription = manager.get_tenant_subscription(tenant_id)

    if not subscription:
        raise HTTPException(status_code=404, detail="No active subscription found")

    try:
        updated = manager.change_plan(
            subscription_id=subscription.id,
            new_plan_id=request.new_plan_id,
            prorate=request.prorate,
        )

        return {
            "id": updated.id,
            "plan_id": updated.plan_id,
            "status": updated.status,
            "message": "Plan changed successfully",
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/tenants/{tenant_id}/subscription/cancel", tags=["Subscriptions"])
async def cancel_subscription(
    tenant_id: str, request: CancelSubscriptionRequest, _=Depends(verify_api_key)
):
    """取消订阅"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    subscription = manager.get_tenant_subscription(tenant_id)

    if not subscription:
        raise HTTPException(status_code=404, detail="No active subscription found")

    try:
        updated = manager.cancel_subscription(
            subscription_id=subscription.id, at_period_end=request.at_period_end
        )

        return {
            "id": updated.id,
            "status": updated.status,
            "cancel_at_period_end": updated.cancel_at_period_end,
            "canceled_at": updated.canceled_at.isoformat() if updated.canceled_at else None,
            "message": "Subscription cancelled",
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


# Usage APIs
@app.post("/api/v1/tenants/{tenant_id}/usage", tags=["Subscriptions"])
async def record_usage(tenant_id: str, request: RecordUsageRequest, _=Depends(verify_api_key)):
    """记录用量"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    record = manager.record_usage(
        tenant_id=tenant_id,
        resource_type=request.resource_type,
        quantity=request.quantity,
        unit=request.unit,
        description=request.description,
    )

    return {
        "id": record.id,
        "tenant_id": record.tenant_id,
        "resource_type": record.resource_type,
        "quantity": record.quantity,
        "unit": record.unit,
        "cost": record.cost,
        "recorded_at": record.recorded_at.isoformat(),
    }


@app.get("/api/v1/tenants/{tenant_id}/usage", tags=["Subscriptions"])
async def get_usage_summary(
    tenant_id: str,
    start_date: str | None = Query(default=None, description="开始日期 (ISO格式)"),
    end_date: str | None = Query(default=None, description="结束日期 (ISO格式)"),
    _=Depends(verify_api_key),
):
    """获取用量汇总"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    start = datetime.fromisoformat(start_date) if start_date else None
    end = datetime.fromisoformat(end_date) if end_date else None

    summary = manager.get_usage_summary(tenant_id, start, end)

    return summary


# Payment APIs
@app.get("/api/v1/tenants/{tenant_id}/payments", tags=["Subscriptions"])
async def list_payments(
    tenant_id: str,
    status: str | None = Query(default=None, description="支付状态过滤"),
    limit: int = Query(default=100, description="返回数量限制"),
    offset: int = Query(default=0, description="偏移量"),
    _=Depends(verify_api_key),
):
    """获取支付记录列表"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    payments = manager.list_payments(tenant_id, status, limit, offset)

    return {
        "payments": [
            {
                "id": p.id,
                "amount": p.amount,
                "currency": p.currency,
                "provider": p.provider,
                "status": p.status,
                "payment_method": p.payment_method,
                "paid_at": p.paid_at.isoformat() if p.paid_at else None,
                "failed_at": p.failed_at.isoformat() if p.failed_at else None,
                "created_at": p.created_at.isoformat(),
            }
            for p in payments
        ],
        "total": len(payments),
    }


@app.get("/api/v1/tenants/{tenant_id}/payments/{payment_id}", tags=["Subscriptions"])
async def get_payment(tenant_id: str, payment_id: str, _=Depends(verify_api_key)):
    """获取支付记录详情"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    payment = manager.get_payment(payment_id)

    if not payment or payment.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Payment not found")

    return {
        "id": payment.id,
        "tenant_id": payment.tenant_id,
        "subscription_id": payment.subscription_id,
        "invoice_id": payment.invoice_id,
        "amount": payment.amount,
        "currency": payment.currency,
        "provider": payment.provider,
        "provider_payment_id": payment.provider_payment_id,
        "status": payment.status,
        "payment_method": payment.payment_method,
        "paid_at": payment.paid_at.isoformat() if payment.paid_at else None,
        "failed_at": payment.failed_at.isoformat() if payment.failed_at else None,
        "failure_reason": payment.failure_reason,
        "created_at": payment.created_at.isoformat(),
    }


# Invoice APIs
@app.get("/api/v1/tenants/{tenant_id}/invoices", tags=["Subscriptions"])
async def list_invoices(
    tenant_id: str,
    status: str | None = Query(default=None, description="发票状态过滤"),
    limit: int = Query(default=100, description="返回数量限制"),
    offset: int = Query(default=0, description="偏移量"),
    _=Depends(verify_api_key),
):
    """获取发票列表"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    invoices = manager.list_invoices(tenant_id, status, limit, offset)

    return {
        "invoices": [
            {
                "id": inv.id,
                "invoice_number": inv.invoice_number,
                "status": inv.status,
                "amount_due": inv.amount_due,
                "amount_paid": inv.amount_paid,
                "currency": inv.currency,
                "period_start": inv.period_start.isoformat() if inv.period_start else None,
                "period_end": inv.period_end.isoformat() if inv.period_end else None,
                "description": inv.description,
                "due_date": inv.due_date.isoformat() if inv.due_date else None,
                "paid_at": inv.paid_at.isoformat() if inv.paid_at else None,
                "created_at": inv.created_at.isoformat(),
            }
            for inv in invoices
        ],
        "total": len(invoices),
    }


@app.get("/api/v1/tenants/{tenant_id}/invoices/{invoice_id}", tags=["Subscriptions"])
async def get_invoice(tenant_id: str, invoice_id: str, _=Depends(verify_api_key)):
    """获取发票详情"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    invoice = manager.get_invoice(invoice_id)

    if not invoice or invoice.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Invoice not found")

    return {
        "id": invoice.id,
        "invoice_number": invoice.invoice_number,
        "status": invoice.status,
        "amount_due": invoice.amount_due,
        "amount_paid": invoice.amount_paid,
        "currency": invoice.currency,
        "period_start": invoice.period_start.isoformat() if invoice.period_start else None,
        "period_end": invoice.period_end.isoformat() if invoice.period_end else None,
        "description": invoice.description,
        "line_items": invoice.line_items,
        "due_date": invoice.due_date.isoformat() if invoice.due_date else None,
        "paid_at": invoice.paid_at.isoformat() if invoice.paid_at else None,
        "voided_at": invoice.voided_at.isoformat() if invoice.voided_at else None,
        "void_reason": invoice.void_reason,
        "created_at": invoice.created_at.isoformat(),
    }


# Refund APIs
@app.post("/api/v1/tenants/{tenant_id}/refunds", tags=["Subscriptions"])
async def request_refund(
    tenant_id: str,
    request: RequestRefundRequest,
    user_id: str = Header(..., description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """申请退款"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    try:
        refund = manager.request_refund(
            tenant_id=tenant_id,
            payment_id=request.payment_id,
            amount=request.amount,
            reason=request.reason,
            requested_by=user_id,
        )

        return {
            "id": refund.id,
            "payment_id": refund.payment_id,
            "amount": refund.amount,
            "currency": refund.currency,
            "reason": refund.reason,
            "status": refund.status,
            "requested_at": refund.requested_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/refunds", tags=["Subscriptions"])
async def list_refunds(
    tenant_id: str,
    status: str | None = Query(default=None, description="退款状态过滤"),
    limit: int = Query(default=100, description="返回数量限制"),
    offset: int = Query(default=0, description="偏移量"),
    _=Depends(verify_api_key),
):
    """获取退款记录列表"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()
    refunds = manager.list_refunds(tenant_id, status, limit, offset)

    return {
        "refunds": [
            {
                "id": r.id,
                "payment_id": r.payment_id,
                "amount": r.amount,
                "currency": r.currency,
                "reason": r.reason,
                "status": r.status,
                "requested_by": r.requested_by,
                "requested_at": r.requested_at.isoformat(),
                "approved_by": r.approved_by,
                "approved_at": r.approved_at.isoformat() if r.approved_at else None,
                "completed_at": r.completed_at.isoformat() if r.completed_at else None,
            }
            for r in refunds
        ],
        "total": len(refunds),
    }


@app.post("/api/v1/tenants/{tenant_id}/refunds/{refund_id}/process", tags=["Subscriptions"])
async def process_refund(
    tenant_id: str,
    refund_id: str,
    request: ProcessRefundRequest,
    user_id: str = Header(..., description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """处理退款申请（管理员）"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    if request.action == "approve":
        refund = manager.approve_refund(refund_id, user_id)
        if not refund:
            raise HTTPException(status_code=404, detail="Refund not found")

        # 自动完成退款（简化实现）
        refund = manager.complete_refund(refund_id)

        return {
            "id": refund.id,
            "status": refund.status,
            "message": "Refund approved and processed",
        }

    elif request.action == "reject":
        if not request.reason:
            raise HTTPException(status_code=400, detail="Rejection reason is required")

        refund = manager.reject_refund(refund_id, request.reason)
        if not refund:
            raise HTTPException(status_code=404, detail="Refund not found")

        return {"id": refund.id, "status": refund.status, "message": "Refund rejected"}

    else:
        raise HTTPException(status_code=400, detail="Invalid action")


# Billing History API
@app.get("/api/v1/tenants/{tenant_id}/billing-history", tags=["Subscriptions"])
async def get_billing_history(
    tenant_id: str,
    start_date: str | None = Query(default=None, description="开始日期 (ISO格式)"),
    end_date: str | None = Query(default=None, description="结束日期 (ISO格式)"),
    limit: int = Query(default=100, description="返回数量限制"),
    offset: int = Query(default=0, description="偏移量"),
    _=Depends(verify_api_key),
):
    """获取账单历史"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    start = datetime.fromisoformat(start_date) if start_date else None
    end = datetime.fromisoformat(end_date) if end_date else None

    history = manager.get_billing_history(tenant_id, start, end, limit, offset)

    return {
        "history": [
            {
                "id": h.id,
                "type": h.type,
                "amount": h.amount,
                "currency": h.currency,
                "description": h.description,
                "reference_id": h.reference_id,
                "balance_after": h.balance_after,
                "created_at": h.created_at.isoformat(),
            }
            for h in history
        ],
        "total": len(history),
    }


# Payment Provider Integration APIs
@app.post("/api/v1/tenants/{tenant_id}/checkout/stripe", tags=["Subscriptions"])
async def create_stripe_checkout(
    tenant_id: str, request: CreateCheckoutSessionRequest, _=Depends(verify_api_key)
):
    """创建 Stripe Checkout 会话"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    try:
        session = manager.create_stripe_checkout_session(
            tenant_id=tenant_id,
            plan_id=request.plan_id,
            success_url=request.success_url,
            cancel_url=request.cancel_url,
            billing_cycle=request.billing_cycle,
        )

        return session
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/tenants/{tenant_id}/checkout/alipay", tags=["Subscriptions"])
async def create_alipay_order(
    tenant_id: str,
    plan_id: str,
    billing_cycle: str = Query(default="monthly", description="计费周期"),
    _=Depends(verify_api_key),
):
    """创建支付宝订单"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    try:
        order = manager.create_alipay_order(
            tenant_id=tenant_id, plan_id=plan_id, billing_cycle=billing_cycle
        )

        return order
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/tenants/{tenant_id}/checkout/wechat", tags=["Subscriptions"])
async def create_wechat_order(
    tenant_id: str,
    plan_id: str,
    billing_cycle: str = Query(default="monthly", description="计费周期"),
    _=Depends(verify_api_key),
):
    """创建微信支付订单"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    manager = get_subscription_manager()

    try:
        order = manager.create_wechat_order(
            tenant_id=tenant_id, plan_id=plan_id, billing_cycle=billing_cycle
        )

        return order
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


# Webhook Handlers
@app.post("/webhooks/stripe", tags=["Subscriptions"])
async def stripe_webhook(request: Request):
    """Stripe Webhook 处理"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    payload = await request.json()
    manager = get_subscription_manager()

    success = manager.handle_webhook("stripe", payload)

    if success:
        return {"status": "ok"}
    else:
        raise HTTPException(status_code=400, detail="Webhook processing failed")


@app.post("/webhooks/alipay", tags=["Subscriptions"])
async def alipay_webhook(request: Request):
    """支付宝 Webhook 处理"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    payload = await request.json()
    manager = get_subscription_manager()

    success = manager.handle_webhook("alipay", payload)

    if success:
        return {"status": "ok"}
    else:
        raise HTTPException(status_code=400, detail="Webhook processing failed")


@app.post("/webhooks/wechat", tags=["Subscriptions"])
async def wechat_webhook(request: Request):
    """微信支付 Webhook 处理"""
    if not SUBSCRIPTION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Subscription manager not available")

    payload = await request.json()
    manager = get_subscription_manager()

    success = manager.handle_webhook("wechat", payload)

    if success:
        return {"status": "ok"}
    else:
        raise HTTPException(status_code=400, detail="Webhook processing failed")


# ==================== Phase 8: Enterprise Features API ====================

# Pydantic Models for Enterprise


class SSOConfigCreate(BaseModel):
    provider: str = Field(
        ..., description="SSO 提供商: wechat_work/dingtalk/feishu/okta/azure_ad/google/custom_saml"
    )
    entity_id: str | None = Field(default=None, description="SAML Entity ID")
    sso_url: str | None = Field(default=None, description="SAML SSO URL")
    slo_url: str | None = Field(default=None, description="SAML SLO URL")
    certificate: str | None = Field(default=None, description="SAML X.509 证书")
    metadata_url: str | None = Field(default=None, description="SAML 元数据 URL")
    metadata_xml: str | None = Field(default=None, description="SAML 元数据 XML")
    client_id: str | None = Field(default=None, description="OAuth Client ID")
    client_secret: str | None = Field(default=None, description="OAuth Client Secret")
    authorization_url: str | None = Field(default=None, description="OAuth 授权 URL")
    token_url: str | None = Field(default=None, description="OAuth Token URL")
    userinfo_url: str | None = Field(default=None, description="OAuth UserInfo URL")
    scopes: list[str] = Field(default=["openid", "email", "profile"], description="OAuth Scopes")
    attribute_mapping: dict[str, str] | None = Field(default=None, description="属性映射")
    auto_provision: bool = Field(default=True, description="自动创建用户")
    default_role: str = Field(default="member", description="默认角色")
    domain_restriction: list[str] = Field(default_factory=list, description="允许的邮箱域名")


class SSOConfigUpdate(BaseModel):
    entity_id: str | None = None
    sso_url: str | None = None
    slo_url: str | None = None
    certificate: str | None = None
    metadata_url: str | None = None
    metadata_xml: str | None = None
    client_id: str | None = None
    client_secret: str | None = None
    authorization_url: str | None = None
    token_url: str | None = None
    userinfo_url: str | None = None
    scopes: list[str] | None = None
    attribute_mapping: dict[str, str] | None = None
    auto_provision: bool | None = None
    default_role: str | None = None
    domain_restriction: list[str] | None = None
    status: str | None = None


class SCIMConfigCreate(BaseModel):
    provider: str = Field(..., description="身份提供商")
    scim_base_url: str = Field(..., description="SCIM 服务端地址")
    scim_token: str = Field(..., description="SCIM 访问令牌")
    sync_interval_minutes: int = Field(default=60, description="同步间隔（分钟）")
    attribute_mapping: dict[str, str] | None = Field(default=None, description="属性映射")
    sync_rules: dict[str, Any] | None = Field(default=None, description="同步规则")


class SCIMConfigUpdate(BaseModel):
    scim_base_url: str | None = None
    scim_token: str | None = None
    sync_interval_minutes: int | None = None
    attribute_mapping: dict[str, str] | None = None
    sync_rules: dict[str, Any] | None = None
    status: str | None = None


class AuditExportCreate(BaseModel):
    export_format: str = Field(..., description="导出格式: json/csv/pdf/xlsx")
    start_date: str = Field(..., description="开始日期 (ISO 格式)")
    end_date: str = Field(..., description="结束日期 (ISO 格式)")
    filters: dict[str, Any] | None = Field(default_factory=dict, description="过滤条件")
    compliance_standard: str | None = Field(
        default=None, description="合规标准: soc2/iso27001/gdpr/hipaa/pci_dss"
    )


class RetentionPolicyCreate(BaseModel):
    name: str = Field(..., description="策略名称")
    description: str | None = Field(default=None, description="策略描述")
    resource_type: str = Field(
        ..., description="资源类型: project/transcript/entity/audit_log/user_data"
    )
    retention_days: int = Field(..., description="保留天数")
    action: str = Field(..., description="动作: archive/delete/anonymize")
    conditions: dict[str, Any] | None = Field(default_factory=dict, description="触发条件")
    auto_execute: bool = Field(default=False, description="自动执行")
    execute_at: str | None = Field(default=None, description="执行时间 (cron 表达式)")
    notify_before_days: int = Field(default=7, description="提前通知天数")
    archive_location: str | None = Field(default=None, description="归档位置")
    archive_encryption: bool = Field(default=True, description="归档加密")


class RetentionPolicyUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    retention_days: int | None = None
    action: str | None = None
    conditions: dict[str, Any] | None = None
    auto_execute: bool | None = None
    execute_at: str | None = None
    notify_before_days: int | None = None
    archive_location: str | None = None
    archive_encryption: bool | None = None
    is_active: bool | None = None


# SSO/SAML APIs


@app.post("/api/v1/tenants/{tenant_id}/sso-configs", tags=["Enterprise"])
async def create_sso_config_endpoint(
    tenant_id: str, config: SSOConfigCreate, _=Depends(verify_api_key)
):
    """创建 SSO 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()

    try:
        sso_config = manager.create_sso_config(
            tenant_id=tenant_id,
            provider=config.provider,
            entity_id=config.entity_id,
            sso_url=config.sso_url,
            slo_url=config.slo_url,
            certificate=config.certificate,
            metadata_url=config.metadata_url,
            metadata_xml=config.metadata_xml,
            client_id=config.client_id,
            client_secret=config.client_secret,
            authorization_url=config.authorization_url,
            token_url=config.token_url,
            userinfo_url=config.userinfo_url,
            scopes=config.scopes,
            attribute_mapping=config.attribute_mapping,
            auto_provision=config.auto_provision,
            default_role=config.default_role,
            domain_restriction=config.domain_restriction,
        )

        return {
            "id": sso_config.id,
            "tenant_id": sso_config.tenant_id,
            "provider": sso_config.provider,
            "status": sso_config.status,
            "entity_id": sso_config.entity_id,
            "sso_url": sso_config.sso_url,
            "authorization_url": sso_config.authorization_url,
            "scopes": sso_config.scopes,
            "auto_provision": sso_config.auto_provision,
            "default_role": sso_config.default_role,
            "created_at": sso_config.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/sso-configs", tags=["Enterprise"])
async def list_sso_configs_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """列出租户的所有 SSO 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    configs = manager.list_sso_configs(tenant_id)

    return {
        "configs": [
            {
                "id": c.id,
                "provider": c.provider,
                "status": c.status,
                "entity_id": c.entity_id,
                "sso_url": c.sso_url,
                "authorization_url": c.authorization_url,
                "auto_provision": c.auto_provision,
                "default_role": c.default_role,
                "created_at": c.created_at.isoformat(),
            }
            for c in configs
        ],
        "total": len(configs),
    }


@app.get("/api/v1/tenants/{tenant_id}/sso-configs/{config_id}", tags=["Enterprise"])
async def get_sso_config_endpoint(tenant_id: str, config_id: str, _=Depends(verify_api_key)):
    """获取 SSO 配置详情"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_sso_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SSO config not found")

    return {
        "id": config.id,
        "tenant_id": config.tenant_id,
        "provider": config.provider,
        "status": config.status,
        "entity_id": config.entity_id,
        "sso_url": config.sso_url,
        "slo_url": config.slo_url,
        "metadata_url": config.metadata_url,
        "authorization_url": config.authorization_url,
        "token_url": config.token_url,
        "userinfo_url": config.userinfo_url,
        "scopes": config.scopes,
        "attribute_mapping": config.attribute_mapping,
        "auto_provision": config.auto_provision,
        "default_role": config.default_role,
        "domain_restriction": config.domain_restriction,
        "created_at": config.created_at.isoformat(),
        "updated_at": config.updated_at.isoformat(),
    }


@app.put("/api/v1/tenants/{tenant_id}/sso-configs/{config_id}", tags=["Enterprise"])
async def update_sso_config_endpoint(
    tenant_id: str, config_id: str, update: SSOConfigUpdate, _=Depends(verify_api_key)
):
    """更新 SSO 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_sso_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SSO config not found")

    updated = manager.update_sso_config(
        config_id=config_id, **{k: v for k, v in update.dict().items() if v is not None}
    )

    return {
        "id": updated.id,
        "status": updated.status,
        "updated_at": updated.updated_at.isoformat(),
    }


@app.delete("/api/v1/tenants/{tenant_id}/sso-configs/{config_id}", tags=["Enterprise"])
async def delete_sso_config_endpoint(tenant_id: str, config_id: str, _=Depends(verify_api_key)):
    """删除 SSO 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_sso_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SSO config not found")

    manager.delete_sso_config(config_id)
    return {"success": True}


@app.get("/api/v1/tenants/{tenant_id}/sso-configs/{config_id}/metadata", tags=["Enterprise"])
async def get_sso_metadata_endpoint(
    tenant_id: str,
    config_id: str,
    base_url: str = Query(..., description="服务基础 URL"),
    _=Depends(verify_api_key),
):
    """获取 SAML Service Provider 元数据"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_sso_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SSO config not found")

    metadata = manager.generate_saml_metadata(config_id, base_url)

    return {
        "metadata_xml": metadata,
        "entity_id": f"{base_url}/api/v1/sso/saml/{tenant_id}",
        "acs_url": f"{base_url}/api/v1/sso/saml/{tenant_id}/acs",
        "slo_url": f"{base_url}/api/v1/sso/saml/{tenant_id}/slo",
    }


# SCIM APIs


@app.post("/api/v1/tenants/{tenant_id}/scim-configs", tags=["Enterprise"])
async def create_scim_config_endpoint(
    tenant_id: str, config: SCIMConfigCreate, _=Depends(verify_api_key)
):
    """创建 SCIM 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()

    try:
        scim_config = manager.create_scim_config(
            tenant_id=tenant_id,
            provider=config.provider,
            scim_base_url=config.scim_base_url,
            scim_token=config.scim_token,
            sync_interval_minutes=config.sync_interval_minutes,
            attribute_mapping=config.attribute_mapping,
            sync_rules=config.sync_rules,
        )

        return {
            "id": scim_config.id,
            "tenant_id": scim_config.tenant_id,
            "provider": scim_config.provider,
            "status": scim_config.status,
            "scim_base_url": scim_config.scim_base_url,
            "sync_interval_minutes": scim_config.sync_interval_minutes,
            "created_at": scim_config.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/scim-configs", tags=["Enterprise"])
async def get_scim_config_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户的 SCIM 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_tenant_scim_config(tenant_id)

    if not config:
        raise HTTPException(status_code=404, detail="SCIM config not found")

    return {
        "id": config.id,
        "tenant_id": config.tenant_id,
        "provider": config.provider,
        "status": config.status,
        "scim_base_url": config.scim_base_url,
        "sync_interval_minutes": config.sync_interval_minutes,
        "last_sync_at": config.last_sync_at.isoformat() if config.last_sync_at else None,
        "last_sync_status": config.last_sync_status,
        "last_sync_users_count": config.last_sync_users_count,
        "created_at": config.created_at.isoformat(),
    }


@app.put("/api/v1/tenants/{tenant_id}/scim-configs/{config_id}", tags=["Enterprise"])
async def update_scim_config_endpoint(
    tenant_id: str, config_id: str, update: SCIMConfigUpdate, _=Depends(verify_api_key)
):
    """更新 SCIM 配置"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_scim_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SCIM config not found")

    updated = manager.update_scim_config(
        config_id=config_id, **{k: v for k, v in update.dict().items() if v is not None}
    )

    return {
        "id": updated.id,
        "status": updated.status,
        "updated_at": updated.updated_at.isoformat(),
    }


@app.post("/api/v1/tenants/{tenant_id}/scim-configs/{config_id}/sync", tags=["Enterprise"])
async def sync_scim_users_endpoint(tenant_id: str, config_id: str, _=Depends(verify_api_key)):
    """执行 SCIM 用户同步"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    config = manager.get_scim_config(config_id)

    if not config or config.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="SCIM config not found")

    result = manager.sync_scim_users(config_id)

    return result


@app.get("/api/v1/tenants/{tenant_id}/scim-users", tags=["Enterprise"])
async def list_scim_users_endpoint(
    tenant_id: str,
    active_only: bool = Query(default=True, description="仅显示活跃用户"),
    _=Depends(verify_api_key),
):
    """列出 SCIM 用户"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    users = manager.list_scim_users(tenant_id, active_only)

    return {
        "users": [
            {
                "id": u.id,
                "external_id": u.external_id,
                "user_name": u.user_name,
                "email": u.email,
                "display_name": u.display_name,
                "active": u.active,
                "groups": u.groups,
                "synced_at": u.synced_at.isoformat(),
            }
            for u in users
        ],
        "total": len(users),
    }


# Audit Log Export APIs


@app.post("/api/v1/tenants/{tenant_id}/audit-exports", tags=["Enterprise"])
async def create_audit_export_endpoint(
    tenant_id: str,
    request: AuditExportCreate,
    current_user: str = Header(default="user", description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """创建审计日志导出任务"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()

    try:
        start_date = datetime.fromisoformat(request.start_date)
        end_date = datetime.fromisoformat(request.end_date)

        export = manager.create_audit_export(
            tenant_id=tenant_id,
            export_format=request.export_format,
            start_date=start_date,
            end_date=end_date,
            created_by=current_user,
            filters=request.filters,
            compliance_standard=request.compliance_standard,
        )

        return {
            "id": export.id,
            "tenant_id": export.tenant_id,
            "export_format": export.export_format,
            "start_date": export.start_date.isoformat(),
            "end_date": export.end_date.isoformat(),
            "compliance_standard": export.compliance_standard,
            "status": export.status,
            "expires_at": export.expires_at.isoformat() if export.expires_at else None,
            "created_at": export.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/audit-exports", tags=["Enterprise"])
async def list_audit_exports_endpoint(
    tenant_id: str,
    limit: int = Query(default=100, description="返回数量限制"),
    _=Depends(verify_api_key),
):
    """列出审计日志导出记录"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    exports = manager.list_audit_exports(tenant_id, limit)

    return {
        "exports": [
            {
                "id": e.id,
                "export_format": e.export_format,
                "start_date": e.start_date.isoformat(),
                "end_date": e.end_date.isoformat(),
                "compliance_standard": e.compliance_standard,
                "status": e.status,
                "file_size": e.file_size,
                "record_count": e.record_count,
                "downloaded_by": e.downloaded_by,
                "expires_at": e.expires_at.isoformat() if e.expires_at else None,
                "created_at": e.created_at.isoformat(),
            }
            for e in exports
        ],
        "total": len(exports),
    }


@app.get("/api/v1/tenants/{tenant_id}/audit-exports/{export_id}", tags=["Enterprise"])
async def get_audit_export_endpoint(tenant_id: str, export_id: str, _=Depends(verify_api_key)):
    """获取审计日志导出详情"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    export = manager.get_audit_export(export_id)

    if not export or export.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Export not found")

    return {
        "id": export.id,
        "export_format": export.export_format,
        "start_date": export.start_date.isoformat(),
        "end_date": export.end_date.isoformat(),
        "compliance_standard": export.compliance_standard,
        "status": export.status,
        "file_path": export.file_path,
        "file_size": export.file_size,
        "record_count": export.record_count,
        "checksum": export.checksum,
        "downloaded_by": export.downloaded_by,
        "downloaded_at": export.downloaded_at.isoformat() if export.downloaded_at else None,
        "expires_at": export.expires_at.isoformat() if export.expires_at else None,
        "created_at": export.created_at.isoformat(),
        "completed_at": export.completed_at.isoformat() if export.completed_at else None,
        "error_message": export.error_message,
    }


@app.post("/api/v1/tenants/{tenant_id}/audit-exports/{export_id}/download", tags=["Enterprise"])
async def download_audit_export_endpoint(
    tenant_id: str,
    export_id: str,
    current_user: str = Header(default="user", description="当前用户ID"),
    _=Depends(verify_api_key),
):
    """下载审计日志导出文件"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    export = manager.get_audit_export(export_id)

    if not export or export.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Export not found")

    if export.status != "completed":
        raise HTTPException(status_code=400, detail="Export not ready")

    # 标记已下载
    manager.mark_export_downloaded(export_id, current_user)

    # 返回文件下载信息
    return {
        "download_url": f"/api/v1/tenants/{tenant_id}/audit-exports/{export_id}/file",
        "expires_at": export.expires_at.isoformat() if export.expires_at else None,
    }


# Data Retention Policy APIs


@app.post("/api/v1/tenants/{tenant_id}/retention-policies", tags=["Enterprise"])
async def create_retention_policy_endpoint(
    tenant_id: str, policy: RetentionPolicyCreate, _=Depends(verify_api_key)
):
    """创建数据保留策略"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()

    try:
        new_policy = manager.create_retention_policy(
            tenant_id=tenant_id,
            name=policy.name,
            resource_type=policy.resource_type,
            retention_days=policy.retention_days,
            action=policy.action,
            description=policy.description,
            conditions=policy.conditions,
            auto_execute=policy.auto_execute,
            execute_at=policy.execute_at,
            notify_before_days=policy.notify_before_days,
            archive_location=policy.archive_location,
            archive_encryption=policy.archive_encryption,
        )

        return {
            "id": new_policy.id,
            "tenant_id": new_policy.tenant_id,
            "name": new_policy.name,
            "resource_type": new_policy.resource_type,
            "retention_days": new_policy.retention_days,
            "action": new_policy.action,
            "auto_execute": new_policy.auto_execute,
            "is_active": new_policy.is_active,
            "created_at": new_policy.created_at.isoformat(),
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/retention-policies", tags=["Enterprise"])
async def list_retention_policies_endpoint(
    tenant_id: str,
    resource_type: str | None = Query(default=None, description="资源类型过滤"),
    _=Depends(verify_api_key),
):
    """列出数据保留策略"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policies = manager.list_retention_policies(tenant_id, resource_type)

    return {
        "policies": [
            {
                "id": p.id,
                "name": p.name,
                "resource_type": p.resource_type,
                "retention_days": p.retention_days,
                "action": p.action,
                "auto_execute": p.auto_execute,
                "is_active": p.is_active,
                "last_executed_at": p.last_executed_at.isoformat() if p.last_executed_at else None,
            }
            for p in policies
        ],
        "total": len(policies),
    }


@app.get("/api/v1/tenants/{tenant_id}/retention-policies/{policy_id}", tags=["Enterprise"])
async def get_retention_policy_endpoint(tenant_id: str, policy_id: str, _=Depends(verify_api_key)):
    """获取数据保留策略详情"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policy = manager.get_retention_policy(policy_id)

    if not policy or policy.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Policy not found")

    return {
        "id": policy.id,
        "tenant_id": policy.tenant_id,
        "name": policy.name,
        "description": policy.description,
        "resource_type": policy.resource_type,
        "retention_days": policy.retention_days,
        "action": policy.action,
        "conditions": policy.conditions,
        "auto_execute": policy.auto_execute,
        "execute_at": policy.execute_at,
        "notify_before_days": policy.notify_before_days,
        "archive_location": policy.archive_location,
        "archive_encryption": policy.archive_encryption,
        "is_active": policy.is_active,
        "last_executed_at": policy.last_executed_at.isoformat()
        if policy.last_executed_at
        else None,
        "last_execution_result": policy.last_execution_result,
        "created_at": policy.created_at.isoformat(),
    }


@app.put("/api/v1/tenants/{tenant_id}/retention-policies/{policy_id}", tags=["Enterprise"])
async def update_retention_policy_endpoint(
    tenant_id: str, policy_id: str, update: RetentionPolicyUpdate, _=Depends(verify_api_key)
):
    """更新数据保留策略"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policy = manager.get_retention_policy(policy_id)

    if not policy or policy.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Policy not found")

    updated = manager.update_retention_policy(
        policy_id=policy_id, **{k: v for k, v in update.dict().items() if v is not None}
    )

    return {"id": updated.id, "updated_at": updated.updated_at.isoformat()}


@app.delete("/api/v1/tenants/{tenant_id}/retention-policies/{policy_id}", tags=["Enterprise"])
async def delete_retention_policy_endpoint(
    tenant_id: str, policy_id: str, _=Depends(verify_api_key)
):
    """删除数据保留策略"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policy = manager.get_retention_policy(policy_id)

    if not policy or policy.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Policy not found")

    manager.delete_retention_policy(policy_id)
    return {"success": True}


@app.post("/api/v1/tenants/{tenant_id}/retention-policies/{policy_id}/execute", tags=["Enterprise"])
async def execute_retention_policy_endpoint(
    tenant_id: str, policy_id: str, _=Depends(verify_api_key)
):
    """执行数据保留策略"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policy = manager.get_retention_policy(policy_id)

    if not policy or policy.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Policy not found")

    job = manager.execute_retention_policy(policy_id)

    return {
        "job_id": job.id,
        "policy_id": job.policy_id,
        "status": job.status,
        "started_at": job.started_at.isoformat() if job.started_at else None,
        "created_at": job.created_at.isoformat(),
    }


@app.get("/api/v1/tenants/{tenant_id}/retention-policies/{policy_id}/jobs", tags=["Enterprise"])
async def list_retention_jobs_endpoint(
    tenant_id: str,
    policy_id: str,
    limit: int = Query(default=100, description="返回数量限制"),
    _=Depends(verify_api_key),
):
    """列出数据保留任务"""
    if not ENTERPRISE_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Enterprise manager not available")

    manager = get_enterprise_manager()
    policy = manager.get_retention_policy(policy_id)

    if not policy or policy.tenant_id != tenant_id:
        raise HTTPException(status_code=404, detail="Policy not found")

    jobs = manager.list_retention_jobs(policy_id, limit)

    return {
        "jobs": [
            {
                "id": j.id,
                "status": j.status,
                "started_at": j.started_at.isoformat() if j.started_at else None,
                "completed_at": j.completed_at.isoformat() if j.completed_at else None,
                "affected_records": j.affected_records,
                "archived_records": j.archived_records,
                "deleted_records": j.deleted_records,
                "error_count": j.error_count,
            }
            for j in jobs
        ],
        "total": len(jobs),
    }


# ============================================
# Phase 8 Task 7: Globalization & Localization API
# ============================================


# Pydantic Models for Localization API
class TranslationCreate(BaseModel):
    key: str = Field(..., description="翻译键")
    value: str = Field(..., description="翻译值")
    namespace: str = Field(default="common", description="命名空间")
    context: str | None = Field(default=None, description="上下文说明")


class TranslationUpdate(BaseModel):
    value: str = Field(..., description="翻译值")
    context: str | None = Field(default=None, description="上下文说明")


class LocalizationSettingsCreate(BaseModel):
    default_language: str = Field(default="en", description="默认语言")
    supported_languages: list[str] = Field(default=["en"], description="支持的语言列表")
    default_currency: str = Field(default="USD", description="默认货币")
    supported_currencies: list[str] = Field(default=["USD"], description="支持的货币列表")
    default_timezone: str = Field(default="UTC", description="默认时区")
    region_code: str = Field(default="global", description="区域代码")
    data_residency: str = Field(default="regional", description="数据驻留策略")


class LocalizationSettingsUpdate(BaseModel):
    default_language: str | None = None
    supported_languages: list[str] | None = None
    default_currency: str | None = None
    supported_currencies: list[str] | None = None
    default_timezone: str | None = None
    region_code: str | None = None
    data_residency: str | None = None


class DataCenterMappingRequest(BaseModel):
    region_code: str = Field(..., description="区域代码")
    data_residency: str = Field(default="regional", description="数据驻留策略")


class FormatDateTimeRequest(BaseModel):
    timestamp: str = Field(..., description="ISO格式时间戳")
    timezone: str | None = Field(default=None, description="目标时区")
    format_type: str = Field(default="datetime", description="格式类型: date/time/datetime")


class FormatNumberRequest(BaseModel):
    number: float = Field(..., description="数字")
    decimal_places: int | None = Field(default=None, description="小数位数")


class FormatCurrencyRequest(BaseModel):
    amount: float = Field(..., description="金额")
    currency: str = Field(..., description="货币代码")


class ConvertTimezoneRequest(BaseModel):
    timestamp: str = Field(..., description="ISO格式时间戳")
    from_tz: str = Field(..., description="源时区")
    to_tz: str = Field(..., description="目标时区")


# Translation APIs
@app.get("/api/v1/translations/{language}/{key}", tags=["Localization"])
async def get_translation(
    language: str,
    key: str,
    namespace: str = Query(default="common", description="命名空间"),
    _=Depends(verify_api_key),
):
    """获取翻译"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    value = manager.get_translation(key, language, namespace)

    if value is None:
        raise HTTPException(status_code=404, detail="Translation not found")

    return {"key": key, "language": language, "namespace": namespace, "value": value}


@app.post("/api/v1/translations/{language}", tags=["Localization"])
async def create_translation(language: str, request: TranslationCreate, _=Depends(verify_api_key)):
    """创建/更新翻译"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    translation = manager.set_translation(
        key=request.key,
        language=language,
        value=request.value,
        namespace=request.namespace,
        context=request.context,
    )

    return {
        "id": translation.id,
        "key": translation.key,
        "language": translation.language,
        "namespace": translation.namespace,
        "value": translation.value,
        "created_at": translation.created_at.isoformat(),
    }


@app.put("/api/v1/translations/{language}/{key}", tags=["Localization"])
async def update_translation(
    language: str,
    key: str,
    request: TranslationUpdate,
    namespace: str = Query(default="common", description="命名空间"),
    _=Depends(verify_api_key),
):
    """更新翻译"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    translation = manager.set_translation(
        key=key,
        language=language,
        value=request.value,
        namespace=namespace,
        context=request.context,
    )

    return {
        "id": translation.id,
        "key": translation.key,
        "language": translation.language,
        "namespace": translation.namespace,
        "value": translation.value,
        "updated_at": translation.updated_at.isoformat(),
    }


@app.delete("/api/v1/translations/{language}/{key}", tags=["Localization"])
async def delete_translation(
    language: str,
    key: str,
    namespace: str = Query(default="common", description="命名空间"),
    _=Depends(verify_api_key),
):
    """删除翻译"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    success = manager.delete_translation(key, language, namespace)

    if not success:
        raise HTTPException(status_code=404, detail="Translation not found")

    return {"success": True, "message": "Translation deleted"}


@app.get("/api/v1/translations", tags=["Localization"])
async def list_translations(
    language: str | None = Query(default=None, description="语言代码"),
    namespace: str | None = Query(default=None, description="命名空间"),
    limit: int = Query(default=1000, description="返回数量限制"),
    offset: int = Query(default=0, description="偏移量"),
    _=Depends(verify_api_key),
):
    """列出翻译"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    translations = manager.list_translations(language, namespace, limit, offset)

    return {
        "translations": [
            {
                "id": t.id,
                "key": t.key,
                "language": t.language,
                "namespace": t.namespace,
                "value": t.value,
                "is_reviewed": t.is_reviewed,
                "updated_at": t.updated_at.isoformat(),
            }
            for t in translations
        ],
        "total": len(translations),
    }


# Language APIs
@app.get("/api/v1/languages", tags=["Localization"])
async def list_languages(active_only: bool = Query(default=True, description="仅返回激活的语言")):
    """列出支持的语言"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    languages = manager.list_language_configs(active_only)

    return {
        "languages": [
            {
                "code": lang.code,
                "name": lang.name,
                "name_local": lang.name_local,
                "is_rtl": lang.is_rtl,
                "is_active": lang.is_active,
                "is_default": lang.is_default,
                "date_format": lang.date_format,
                "time_format": lang.time_format,
                "calendar_type": lang.calendar_type,
            }
            for lang in languages
        ],
        "total": len(languages),
    }


@app.get("/api/v1/languages/{code}", tags=["Localization"])
async def get_language(code: str):
    """获取语言详情"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    lang = manager.get_language_config(code)

    if not lang:
        raise HTTPException(status_code=404, detail="Language not found")

    return {
        "code": lang.code,
        "name": lang.name,
        "name_local": lang.name_local,
        "is_rtl": lang.is_rtl,
        "is_active": lang.is_active,
        "is_default": lang.is_default,
        "fallback_language": lang.fallback_language,
        "date_format": lang.date_format,
        "time_format": lang.time_format,
        "datetime_format": lang.datetime_format,
        "number_format": lang.number_format,
        "currency_format": lang.currency_format,
        "first_day_of_week": lang.first_day_of_week,
        "calendar_type": lang.calendar_type,
    }


# Data Center APIs
@app.get("/api/v1/data-centers", tags=["Localization"])
async def list_data_centers(
    status: str | None = Query(default=None, description="状态过滤"),
    region: str | None = Query(default=None, description="区域过滤"),
):
    """列出数据中心"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    data_centers = manager.list_data_centers(status, region)

    return {
        "data_centers": [
            {
                "id": dc.id,
                "region_code": dc.region_code,
                "name": dc.name,
                "location": dc.location,
                "endpoint": dc.endpoint,
                "status": dc.status,
                "priority": dc.priority,
                "supported_regions": dc.supported_regions,
            }
            for dc in data_centers
        ],
        "total": len(data_centers),
    }


@app.get("/api/v1/data-centers/{dc_id}", tags=["Localization"])
async def get_data_center(dc_id: str):
    """获取数据中心详情"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    dc = manager.get_data_center(dc_id)

    if not dc:
        raise HTTPException(status_code=404, detail="Data center not found")

    return {
        "id": dc.id,
        "region_code": dc.region_code,
        "name": dc.name,
        "location": dc.location,
        "endpoint": dc.endpoint,
        "status": dc.status,
        "priority": dc.priority,
        "supported_regions": dc.supported_regions,
        "capabilities": dc.capabilities,
    }


@app.get("/api/v1/tenants/{tenant_id}/data-center", tags=["Localization"])
async def get_tenant_data_center(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户数据中心配置"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    mapping = manager.get_tenant_data_center(tenant_id)

    if not mapping:
        raise HTTPException(status_code=404, detail="Data center mapping not found")

    # 获取数据中心详情
    primary_dc = manager.get_data_center(mapping.primary_dc_id)
    secondary_dc = (
        manager.get_data_center(mapping.secondary_dc_id) if mapping.secondary_dc_id else None
    )

    return {
        "id": mapping.id,
        "tenant_id": mapping.tenant_id,
        "region_code": mapping.region_code,
        "data_residency": mapping.data_residency,
        "primary_dc": (
            {
                "id": primary_dc.id,
                "region_code": primary_dc.region_code,
                "name": primary_dc.name,
                "endpoint": primary_dc.endpoint,
            }
            if primary_dc
            else None
        ),
        "secondary_dc": (
            {
                "id": secondary_dc.id,
                "region_code": secondary_dc.region_code,
                "name": secondary_dc.name,
                "endpoint": secondary_dc.endpoint,
            }
            if secondary_dc
            else None
        ),
        "created_at": mapping.created_at.isoformat(),
    }


@app.post("/api/v1/tenants/{tenant_id}/data-center", tags=["Localization"])
async def set_tenant_data_center(
    tenant_id: str, request: DataCenterMappingRequest, _=Depends(verify_api_key)
):
    """设置租户数据中心"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    mapping = manager.set_tenant_data_center(
        tenant_id=tenant_id, region_code=request.region_code, data_residency=request.data_residency
    )

    return {
        "id": mapping.id,
        "tenant_id": mapping.tenant_id,
        "region_code": mapping.region_code,
        "data_residency": mapping.data_residency,
        "created_at": mapping.created_at.isoformat(),
    }


# Payment Method APIs
@app.get("/api/v1/payment-methods", tags=["Localization"])
async def list_payment_methods(
    country_code: str | None = Query(default=None, description="国家代码"),
    currency: str | None = Query(default=None, description="货币代码"),
    active_only: bool = Query(default=True, description="仅返回激活的支付方式"),
):
    """列出支付方式"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    methods = manager.list_payment_methods(country_code, currency, active_only)

    return {
        "payment_methods": [
            {
                "id": m.id,
                "provider": m.provider,
                "name": m.name,
                "name_local": m.name_local,
                "supported_countries": m.supported_countries,
                "supported_currencies": m.supported_currencies,
                "is_active": m.is_active,
                "display_order": m.display_order,
                "min_amount": m.min_amount,
                "max_amount": m.max_amount,
            }
            for m in methods
        ],
        "total": len(methods),
    }


@app.get("/api/v1/payment-methods/localized", tags=["Localization"])
async def get_localized_payment_methods(
    country_code: str = Query(..., description="国家代码"),
    language: str = Query(default="en", description="语言代码"),
):
    """获取本地化的支付方式列表"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    methods = manager.get_localized_payment_methods(country_code, language)

    return {"country_code": country_code, "language": language, "payment_methods": methods}


# Country APIs
@app.get("/api/v1/countries", tags=["Localization"])
async def list_countries(
    region: str | None = Query(default=None, description="区域过滤"),
    active_only: bool = Query(default=True, description="仅返回激活的国家"),
):
    """列出国家/地区"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    countries = manager.list_country_configs(region, active_only)

    return {
        "countries": [
            {
                "code": c.code,
                "code3": c.code3,
                "name": c.name,
                "region": c.region,
                "default_language": c.default_language,
                "default_currency": c.default_currency,
                "timezone": c.timezone,
                "calendar_type": c.calendar_type,
                "vat_rate": c.vat_rate,
            }
            for c in countries
        ],
        "total": len(countries),
    }


@app.get("/api/v1/countries/{code}", tags=["Localization"])
async def get_country(code: str):
    """获取国家详情"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    country = manager.get_country_config(code)

    if not country:
        raise HTTPException(status_code=404, detail="Country not found")

    return {
        "code": country.code,
        "code3": country.code3,
        "name": country.name,
        "name_local": country.name_local,
        "region": country.region,
        "default_language": country.default_language,
        "supported_languages": country.supported_languages,
        "default_currency": country.default_currency,
        "supported_currencies": country.supported_currencies,
        "timezone": country.timezone,
        "calendar_type": country.calendar_type,
        "vat_rate": country.vat_rate,
    }


# Localization Settings APIs
@app.get("/api/v1/tenants/{tenant_id}/localization", tags=["Localization"])
async def get_localization_settings(tenant_id: str, _=Depends(verify_api_key)):
    """获取租户本地化设置"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    settings = manager.get_localization_settings(tenant_id)

    if not settings:
        raise HTTPException(status_code=404, detail="Localization settings not found")

    return {
        "id": settings.id,
        "tenant_id": settings.tenant_id,
        "default_language": settings.default_language,
        "supported_languages": settings.supported_languages,
        "default_currency": settings.default_currency,
        "supported_currencies": settings.supported_currencies,
        "default_timezone": settings.default_timezone,
        "default_date_format": settings.default_date_format,
        "default_time_format": settings.default_time_format,
        "calendar_type": settings.calendar_type,
        "first_day_of_week": settings.first_day_of_week,
        "region_code": settings.region_code,
        "data_residency": settings.data_residency,
        "updated_at": settings.updated_at.isoformat(),
    }


@app.post("/api/v1/tenants/{tenant_id}/localization", tags=["Localization"])
async def create_localization_settings(
    tenant_id: str, request: LocalizationSettingsCreate, _=Depends(verify_api_key)
):
    """创建租户本地化设置"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    settings = manager.create_localization_settings(
        tenant_id=tenant_id,
        default_language=request.default_language,
        supported_languages=request.supported_languages,
        default_currency=request.default_currency,
        supported_currencies=request.supported_currencies,
        default_timezone=request.default_timezone,
        region_code=request.region_code,
        data_residency=request.data_residency,
    )

    return {
        "id": settings.id,
        "tenant_id": settings.tenant_id,
        "default_language": settings.default_language,
        "supported_languages": settings.supported_languages,
        "default_currency": settings.default_currency,
        "supported_currencies": settings.supported_currencies,
        "default_timezone": settings.default_timezone,
        "region_code": settings.region_code,
        "data_residency": settings.data_residency,
        "created_at": settings.created_at.isoformat(),
    }


@app.put("/api/v1/tenants/{tenant_id}/localization", tags=["Localization"])
async def update_localization_settings(
    tenant_id: str, request: LocalizationSettingsUpdate, _=Depends(verify_api_key)
):
    """更新租户本地化设置"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()

    update_data = {k: v for k, v in request.dict().items() if v is not None}
    settings = manager.update_localization_settings(tenant_id, **update_data)

    if not settings:
        raise HTTPException(status_code=404, detail="Localization settings not found")

    return {
        "id": settings.id,
        "tenant_id": settings.tenant_id,
        "default_language": settings.default_language,
        "supported_languages": settings.supported_languages,
        "default_currency": settings.default_currency,
        "supported_currencies": settings.supported_currencies,
        "default_timezone": settings.default_timezone,
        "region_code": settings.region_code,
        "data_residency": settings.data_residency,
        "updated_at": settings.updated_at.isoformat(),
    }


# Formatting APIs
@app.post("/api/v1/format/datetime", tags=["Localization"])
async def format_datetime_endpoint(
    request: FormatDateTimeRequest, language: str = Query(default="en", description="语言代码")
):
    """格式化日期时间"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()

    try:
        dt = datetime.fromisoformat(request.timestamp.replace("Z", "+00:00"))
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid timestamp format")

    formatted = manager.format_datetime(
        dt=dt, language=language, timezone=request.timezone, format_type=request.format_type
    )

    return {
        "original": request.timestamp,
        "formatted": formatted,
        "language": language,
        "timezone": request.timezone,
        "format_type": request.format_type,
    }


@app.post("/api/v1/format/number", tags=["Localization"])
async def format_number_endpoint(
    request: FormatNumberRequest, language: str = Query(default="en", description="语言代码")
):
    """格式化数字"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    formatted = manager.format_number(
        number=request.number, language=language, decimal_places=request.decimal_places
    )

    return {"original": request.number, "formatted": formatted, "language": language}


@app.post("/api/v1/format/currency", tags=["Localization"])
async def format_currency_endpoint(
    request: FormatCurrencyRequest, language: str = Query(default="en", description="语言代码")
):
    """格式化货币"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    formatted = manager.format_currency(
        amount=request.amount, currency=request.currency, language=language
    )

    return {
        "original": request.amount,
        "currency": request.currency,
        "formatted": formatted,
        "language": language,
    }


@app.post("/api/v1/convert/timezone", tags=["Localization"])
async def convert_timezone_endpoint(request: ConvertTimezoneRequest):
    """转换时区"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()

    try:
        dt = datetime.fromisoformat(request.timestamp.replace("Z", "+00:00"))
    except ValueError:
        raise HTTPException(status_code=400, detail="Invalid timestamp format")

    converted = manager.convert_timezone(dt=dt, from_tz=request.from_tz, to_tz=request.to_tz)

    return {
        "original": request.timestamp,
        "from_timezone": request.from_tz,
        "to_timezone": request.to_tz,
        "converted": converted.isoformat(),
    }


@app.get("/api/v1/detect/locale", tags=["Localization"])
async def detect_locale(
    accept_language: str | None = Header(default=None, description="Accept-Language 头"),
    ip_country: str | None = Query(default=None, description="IP国家代码"),
):
    """检测用户本地化偏好"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    preferences = manager.detect_user_preferences(
        accept_language=accept_language, ip_country=ip_country
    )

    return preferences


@app.get("/api/v1/calendar/{calendar_type}", tags=["Localization"])
async def get_calendar_info(
    calendar_type: str,
    year: int = Query(..., description="年份"),
    month: int = Query(..., description="月份"),
):
    """获取日历信息"""
    if not LOCALIZATION_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="Localization manager not available")

    manager = get_localization_manager()
    info = manager.get_calendar_info(calendar_type, year, month)

    return info


# ============================================
# Phase 8 Task 4: AI 能力增强 API
# ============================================


class CreateCustomModelRequest(BaseModel):
    name: str
    description: str
    model_type: str
    training_data: dict
    hyperparameters: dict = Field(default_factory=lambda: {"epochs": 10, "learning_rate": 0.001})


class AddTrainingSampleRequest(BaseModel):
    text: str
    entities: list[dict]
    metadata: dict = Field(default_factory=dict)


class TrainModelRequest(BaseModel):
    model_id: str


class PredictRequest(BaseModel):
    model_id: str
    text: str


class MultimodalAnalysisRequest(BaseModel):
    provider: str
    input_type: str
    input_urls: list[str]
    prompt: str


class CreateKGRAGRequest(BaseModel):
    name: str
    description: str
    kg_config: dict
    retrieval_config: dict
    generation_config: dict


class KGRAGQueryRequest(BaseModel):
    rag_id: str
    query: str


class SmartSummaryRequest(BaseModel):
    source_type: str
    source_id: str
    summary_type: str
    content_data: dict


class CreatePredictionModelRequest(BaseModel):
    name: str
    prediction_type: str
    target_entity_type: str | None = None
    features: list[str]
    model_config: dict


class PredictDataRequest(BaseModel):
    model_id: str
    input_data: dict


class PredictionFeedbackRequest(BaseModel):
    prediction_id: str
    actual_value: str
    is_correct: bool


# 自定义模型管理 API
@app.post("/api/v1/tenants/{tenant_id}/ai/custom-models", tags=["AI Enhancement"])
async def create_custom_model(
    tenant_id: str,
    request: CreateCustomModelRequest,
    created_by: str = Query(..., description="创建者ID"),
):
    """创建自定义模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        model = manager.create_custom_model(
            tenant_id=tenant_id,
            name=request.name,
            description=request.description,
            model_type=ModelType(request.model_type),
            training_data=request.training_data,
            hyperparameters=request.hyperparameters,
            created_by=created_by,
        )
        return {
            "id": model.id,
            "name": model.name,
            "model_type": model.model_type.value,
            "status": model.status.value,
            "created_at": model.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/ai/custom-models", tags=["AI Enhancement"])
async def list_custom_models(
    tenant_id: str,
    model_type: str | None = Query(default=None, description="模型类型过滤"),
    status: str | None = Query(default=None, description="状态过滤"),
):
    """列出自定义模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    model_type_enum = ModelType(model_type) if model_type else None
    status_enum = ModelStatus(status) if status else None

    models = manager.list_custom_models(tenant_id, model_type_enum, status_enum)

    return {
        "models": [
            {
                "id": m.id,
                "name": m.name,
                "model_type": m.model_type.value,
                "status": m.status.value,
                "metrics": m.metrics,
                "created_at": m.created_at,
            }
            for m in models
        ]
    }


@app.get("/api/v1/ai/custom-models/{model_id}", tags=["AI Enhancement"])
async def get_custom_model(model_id: str):
    """获取自定义模型详情"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    model = manager.get_custom_model(model_id)

    if not model:
        raise HTTPException(status_code=404, detail="Model not found")

    return {
        "id": model.id,
        "tenant_id": model.tenant_id,
        "name": model.name,
        "description": model.description,
        "model_type": model.model_type.value,
        "status": model.status.value,
        "training_data": model.training_data,
        "hyperparameters": model.hyperparameters,
        "metrics": model.metrics,
        "model_path": model.model_path,
        "created_at": model.created_at,
        "trained_at": model.trained_at,
        "created_by": model.created_by,
    }


@app.post("/api/v1/ai/custom-models/{model_id}/samples", tags=["AI Enhancement"])
async def add_training_sample(model_id: str, request: AddTrainingSampleRequest):
    """添加训练样本"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    sample = manager.add_training_sample(
        model_id=model_id, text=request.text, entities=request.entities, metadata=request.metadata
    )

    return {
        "id": sample.id,
        "model_id": sample.model_id,
        "text": sample.text,
        "entities": sample.entities,
        "created_at": sample.created_at,
    }


@app.get("/api/v1/ai/custom-models/{model_id}/samples", tags=["AI Enhancement"])
async def get_training_samples(model_id: str):
    """获取训练样本"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    samples = manager.get_training_samples(model_id)

    return {
        "samples": [
            {
                "id": s.id,
                "text": s.text,
                "entities": s.entities,
                "metadata": s.metadata,
                "created_at": s.created_at,
            }
            for s in samples
        ]
    }


@app.post("/api/v1/ai/custom-models/{model_id}/train", tags=["AI Enhancement"])
async def train_custom_model(model_id: str):
    """训练自定义模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        model = await manager.train_custom_model(model_id)
        return {
            "id": model.id,
            "status": model.status.value,
            "metrics": model.metrics,
            "trained_at": model.trained_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/ai/custom-models/predict", tags=["AI Enhancement"])
async def predict_with_custom_model(request: PredictRequest):
    """使用自定义模型预测"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        entities = await manager.predict_with_custom_model(request.model_id, request.text)
        return {"model_id": request.model_id, "text": request.text, "entities": entities}
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


# 多模态分析 API
@app.post(
    "/api/v1/tenants/{tenant_id}/projects/{project_id}/ai/multimodal", tags=["AI Enhancement"]
)
async def analyze_multimodal(tenant_id: str, project_id: str, request: MultimodalAnalysisRequest):
    """多模态分析"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        analysis = await manager.analyze_multimodal(
            tenant_id=tenant_id,
            project_id=project_id,
            provider=MultimodalProvider(request.provider),
            input_type=request.input_type,
            input_urls=request.input_urls,
            prompt=request.prompt,
        )

        return {
            "id": analysis.id,
            "provider": analysis.provider.value,
            "input_type": analysis.input_type,
            "result": analysis.result,
            "tokens_used": analysis.tokens_used,
            "cost": analysis.cost,
            "created_at": analysis.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/ai/multimodal", tags=["AI Enhancement"])
async def list_multimodal_analyses(
    tenant_id: str, project_id: str | None = Query(default=None, description="项目ID过滤")
):
    """获取多模态分析历史"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    analyses = manager.get_multimodal_analyses(tenant_id, project_id)

    return {
        "analyses": [
            {
                "id": a.id,
                "project_id": a.project_id,
                "provider": a.provider.value,
                "input_type": a.input_type,
                "prompt": a.prompt,
                "result": a.result,
                "tokens_used": a.tokens_used,
                "cost": a.cost,
                "created_at": a.created_at,
            }
            for a in analyses
        ]
    }


# 知识图谱 RAG API
@app.post("/api/v1/tenants/{tenant_id}/projects/{project_id}/ai/kg-rag", tags=["AI Enhancement"])
async def create_kg_rag(tenant_id: str, project_id: str, request: CreateKGRAGRequest):
    """创建知识图谱 RAG 配置"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    rag = manager.create_kg_rag(
        tenant_id=tenant_id,
        project_id=project_id,
        name=request.name,
        description=request.description,
        kg_config=request.kg_config,
        retrieval_config=request.retrieval_config,
        generation_config=request.generation_config,
    )

    return {
        "id": rag.id,
        "name": rag.name,
        "description": rag.description,
        "is_active": rag.is_active,
        "created_at": rag.created_at,
    }


@app.get("/api/v1/tenants/{tenant_id}/ai/kg-rag", tags=["AI Enhancement"])
async def list_kg_rags(
    tenant_id: str, project_id: str | None = Query(default=None, description="项目ID过滤")
):
    """列出知识图谱 RAG 配置"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    rags = manager.list_kg_rags(tenant_id, project_id)

    return {
        "rags": [
            {
                "id": r.id,
                "project_id": r.project_id,
                "name": r.name,
                "description": r.description,
                "is_active": r.is_active,
                "created_at": r.created_at,
            }
            for r in rags
        ]
    }


@app.post("/api/v1/ai/kg-rag/query", tags=["AI Enhancement"])
async def query_kg_rag(
    request: KGRAGQueryRequest,
    project_entities: list[dict] = Body(default=[], description="项目实体列表"),
    project_relations: list[dict] = Body(default=[], description="项目关系列表"),
):
    """基于知识图谱的 RAG 查询"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        result = await manager.query_kg_rag(
            rag_id=request.rag_id,
            query=request.query,
            project_entities=project_entities,
            project_relations=project_relations,
        )

        return {
            "id": result.id,
            "rag_id": result.rag_id,
            "query": result.query,
            "answer": result.answer,
            "sources": result.sources,
            "confidence": result.confidence,
            "tokens_used": result.tokens_used,
            "latency_ms": result.latency_ms,
            "created_at": result.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


# 智能摘要 API
@app.post("/api/v1/tenants/{tenant_id}/projects/{project_id}/ai/summarize", tags=["AI Enhancement"])
async def generate_smart_summary(tenant_id: str, project_id: str, request: SmartSummaryRequest):
    """生成智能摘要"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    summary = await manager.generate_smart_summary(
        tenant_id=tenant_id,
        project_id=project_id,
        source_type=request.source_type,
        source_id=request.source_id,
        summary_type=request.summary_type,
        content_data=request.content_data,
    )

    return {
        "id": summary.id,
        "source_type": summary.source_type,
        "source_id": summary.source_id,
        "summary_type": summary.summary_type,
        "content": summary.content,
        "key_points": summary.key_points,
        "entities_mentioned": summary.entities_mentioned,
        "confidence": summary.confidence,
        "tokens_used": summary.tokens_used,
        "created_at": summary.created_at,
    }


@app.get("/api/v1/tenants/{tenant_id}/projects/{project_id}/ai/summaries", tags=["AI Enhancement"])
async def list_smart_summaries(
    tenant_id: str,
    project_id: str,
    source_type: str | None = Query(default=None, description="来源类型过滤"),
    source_id: str | None = Query(default=None, description="来源ID过滤"),
):
    """获取智能摘要列表"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    get_ai_manager()

    # 这里需要从数据库查询，暂时返回空列表
    return {"summaries": []}


# 预测模型 API
@app.post(
    "/api/v1/tenants/{tenant_id}/projects/{project_id}/ai/prediction-models",
    tags=["AI Enhancement"],
)
async def create_prediction_model(
    tenant_id: str, project_id: str, request: CreatePredictionModelRequest
):
    """创建预测模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        model = manager.create_prediction_model(
            tenant_id=tenant_id,
            project_id=project_id,
            name=request.name,
            prediction_type=PredictionType(request.prediction_type),
            target_entity_type=request.target_entity_type,
            features=request.features,
            model_config=request.model_config,
        )

        return {
            "id": model.id,
            "name": model.name,
            "prediction_type": model.prediction_type.value,
            "target_entity_type": model.target_entity_type,
            "features": model.features,
            "is_active": model.is_active,
            "created_at": model.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/tenants/{tenant_id}/ai/prediction-models", tags=["AI Enhancement"])
async def list_prediction_models(
    tenant_id: str, project_id: str | None = Query(default=None, description="项目ID过滤")
):
    """列出预测模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    models = manager.list_prediction_models(tenant_id, project_id)

    return {
        "models": [
            {
                "id": m.id,
                "project_id": m.project_id,
                "name": m.name,
                "prediction_type": m.prediction_type.value,
                "target_entity_type": m.target_entity_type,
                "features": m.features,
                "accuracy": m.accuracy,
                "last_trained_at": m.last_trained_at,
                "prediction_count": m.prediction_count,
                "is_active": m.is_active,
            }
            for m in models
        ]
    }


@app.get("/api/v1/ai/prediction-models/{model_id}", tags=["AI Enhancement"])
async def get_prediction_model(model_id: str):
    """获取预测模型详情"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    model = manager.get_prediction_model(model_id)

    if not model:
        raise HTTPException(status_code=404, detail="Model not found")

    return {
        "id": model.id,
        "tenant_id": model.tenant_id,
        "project_id": model.project_id,
        "name": model.name,
        "prediction_type": model.prediction_type.value,
        "target_entity_type": model.target_entity_type,
        "features": model.features,
        "model_config": model.model_config,
        "accuracy": model.accuracy,
        "last_trained_at": model.last_trained_at,
        "prediction_count": model.prediction_count,
        "is_active": model.is_active,
        "created_at": model.created_at,
    }


@app.post("/api/v1/ai/prediction-models/{model_id}/train", tags=["AI Enhancement"])
async def train_prediction_model(
    model_id: str, historical_data: list[dict] = Body(..., description="历史训练数据")
):
    """训练预测模型"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        model = await manager.train_prediction_model(model_id, historical_data)
        return {
            "id": model.id,
            "accuracy": model.accuracy,
            "last_trained_at": model.last_trained_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/ai/prediction-models/predict", tags=["AI Enhancement"])
async def predict(request: PredictDataRequest):
    """进行预测"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()

    try:
        result = await manager.predict(request.model_id, request.input_data)

        return {
            "id": result.id,
            "model_id": result.model_id,
            "prediction_type": result.prediction_type.value,
            "target_id": result.target_id,
            "prediction_data": result.prediction_data,
            "confidence": result.confidence,
            "explanation": result.explanation,
            "created_at": result.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ai/prediction-models/{model_id}/results", tags=["AI Enhancement"])
async def get_prediction_results(
    model_id: str, limit: int = Query(default=100, description="返回结果数量限制")
):
    """获取预测结果历史"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    results = manager.get_prediction_results(model_id, limit)

    return {
        "results": [
            {
                "id": r.id,
                "prediction_type": r.prediction_type.value,
                "target_id": r.target_id,
                "prediction_data": r.prediction_data,
                "confidence": r.confidence,
                "explanation": r.explanation,
                "actual_value": r.actual_value,
                "is_correct": r.is_correct,
                "created_at": r.created_at,
            }
            for r in results
        ]
    }


@app.post("/api/v1/ai/prediction-results/feedback", tags=["AI Enhancement"])
async def update_prediction_feedback(request: PredictionFeedbackRequest):
    """更新预测反馈"""
    if not AI_MANAGER_AVAILABLE:
        raise HTTPException(status_code=500, detail="AI manager not available")

    manager = get_ai_manager()
    manager.update_prediction_feedback(
        prediction_id=request.prediction_id,
        actual_value=request.actual_value,
        is_correct=request.is_correct,
    )

    return {"status": "success", "message": "Feedback updated"}


# ==================== Phase 8 Task 5: Growth & Analytics Endpoints ====================


# Pydantic Models for Growth API
class TrackEventRequest(BaseModel):
    tenant_id: str
    user_id: str
    event_type: str
    event_name: str
    properties: dict = Field(default_factory=dict)
    session_id: str | None = None
    device_info: dict = Field(default_factory=dict)
    referrer: str | None = None
    utm_source: str | None = None
    utm_medium: str | None = None
    utm_campaign: str | None = None


class CreateFunnelRequest(BaseModel):
    name: str
    description: str = ""
    steps: list[dict]  # [{"name": "", "event_name": ""}]


class CreateExperimentRequest(BaseModel):
    name: str
    description: str = ""
    hypothesis: str = ""
    variants: list[dict]  # [{"id": "", "name": "", "is_control": true/false}]
    traffic_allocation: str = "random"  # random, stratified, targeted
    traffic_split: dict[str, float] = Field(default_factory=dict)
    target_audience: dict = Field(default_factory=dict)
    primary_metric: str
    secondary_metrics: list[str] = Field(default_factory=list)
    min_sample_size: int = 100
    confidence_level: float = 0.95


class AssignVariantRequest(BaseModel):
    user_id: str
    user_attributes: dict = Field(default_factory=dict)


class RecordMetricRequest(BaseModel):
    variant_id: str
    user_id: str
    metric_name: str
    metric_value: float


class CreateEmailTemplateRequest(BaseModel):
    name: str
    template_type: str  # welcome, onboarding, feature_announcement, churn_recovery, etc.
    subject: str
    html_content: str
    text_content: str | None = None
    variables: list[str] = Field(default_factory=list)
    from_name: str = "InsightFlow"
    from_email: str = "noreply@insightflow.io"
    reply_to: str | None = None


class CreateCampaignRequest(BaseModel):
    name: str
    template_id: str
    recipients: list[dict]  # [{"user_id": "", "email": ""}]
    scheduled_at: str | None = None


class CreateAutomationWorkflowRequest(BaseModel):
    name: str
    description: str = ""
    trigger_type: str  # user_signup, user_login, subscription_created, inactivity, etc.
    trigger_conditions: dict = Field(default_factory=dict)
    actions: list[dict]  # [{"type": "send_email", "template_id": ""}]


class CreateReferralProgramRequest(BaseModel):
    name: str
    description: str = ""
    referrer_reward_type: str  # credit, discount, feature
    referrer_reward_value: float
    referee_reward_type: str
    referee_reward_value: float
    max_referrals_per_user: int = 10
    referral_code_length: int = 8
    expiry_days: int = 30


class ApplyReferralCodeRequest(BaseModel):
    referral_code: str
    referee_id: str


class CreateTeamIncentiveRequest(BaseModel):
    name: str
    description: str = ""
    target_tier: str
    min_team_size: int
    incentive_type: str  # credit, discount, feature
    incentive_value: float
    valid_from: str
    valid_until: str


# Growth Manager singleton
_growth_manager = None


def get_growth_manager_instance():
    global _growth_manager
    if _growth_manager is None and GROWTH_MANAGER_AVAILABLE:
        _growth_manager = GrowthManager()
    return _growth_manager


# ==================== 用户行为分析 API ====================


@app.post("/api/v1/analytics/track", tags=["Growth & Analytics"])
async def track_event_endpoint(request: TrackEventRequest):
    """
    追踪用户事件

    用于记录用户行为，如页面浏览、功能使用、转化等
    """
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    try:
        event = await manager.track_event(
            tenant_id=request.tenant_id,
            user_id=request.user_id,
            event_type=EventType(request.event_type),
            event_name=request.event_name,
            properties=request.properties,
            session_id=request.session_id,
            device_info=request.device_info,
            referrer=request.referrer,
            utm_params=(
                {
                    "source": request.utm_source,
                    "medium": request.utm_medium,
                    "campaign": request.utm_campaign,
                }
                if any([request.utm_source, request.utm_medium, request.utm_campaign])
                else None
            ),
        )

        return {"success": True, "event_id": event.id, "timestamp": event.timestamp.isoformat()}
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


@app.get("/api/v1/analytics/dashboard/{tenant_id}", tags=["Growth & Analytics"])
async def get_analytics_dashboard(tenant_id: str):
    """获取实时分析仪表板数据"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    dashboard = manager.get_realtime_dashboard(tenant_id)

    return dashboard


@app.get("/api/v1/analytics/summary/{tenant_id}", tags=["Growth & Analytics"])
async def get_analytics_summary(
    tenant_id: str, start_date: str | None = None, end_date: str | None = None
):
    """获取用户分析汇总"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    start = datetime.fromisoformat(start_date) if start_date else None
    end = datetime.fromisoformat(end_date) if end_date else None

    summary = manager.get_user_analytics_summary(tenant_id, start, end)

    return summary


@app.get("/api/v1/analytics/user-profile/{tenant_id}/{user_id}", tags=["Growth & Analytics"])
async def get_user_profile(tenant_id: str, user_id: str):
    """获取用户画像"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    profile = manager.get_user_profile(tenant_id, user_id)

    if not profile:
        raise HTTPException(status_code=404, detail="User profile not found")

    return {
        "id": profile.id,
        "user_id": profile.user_id,
        "first_seen": profile.first_seen.isoformat(),
        "last_seen": profile.last_seen.isoformat(),
        "total_sessions": profile.total_sessions,
        "total_events": profile.total_events,
        "feature_usage": profile.feature_usage,
        "ltv": profile.ltv,
        "churn_risk_score": profile.churn_risk_score,
        "engagement_score": profile.engagement_score,
    }


# ==================== 转化漏斗 API ====================


@app.post("/api/v1/analytics/funnels", tags=["Growth & Analytics"])
async def create_funnel_endpoint(request: CreateFunnelRequest, created_by: str = "system"):
    """创建转化漏斗"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    # Note: tenant_id should come from auth context
    tenant_id = "default_tenant"  # Placeholder

    funnel = manager.create_funnel(
        tenant_id=tenant_id,
        name=request.name,
        description=request.description,
        steps=request.steps,
        created_by=created_by,
    )

    return {
        "id": funnel.id,
        "name": funnel.name,
        "steps": funnel.steps,
        "created_at": funnel.created_at,
    }


@app.get("/api/v1/analytics/funnels/{funnel_id}/analyze", tags=["Growth & Analytics"])
async def analyze_funnel_endpoint(
    funnel_id: str, period_start: str | None = None, period_end: str | None = None
):
    """分析漏斗转化率"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    start = datetime.fromisoformat(period_start) if period_start else None
    end = datetime.fromisoformat(period_end) if period_end else None

    analysis = manager.analyze_funnel(funnel_id, start, end)

    if not analysis:
        raise HTTPException(status_code=404, detail="Funnel not found")

    return {
        "funnel_id": analysis.funnel_id,
        "period_start": analysis.period_start.isoformat() if analysis.period_start else None,
        "period_end": analysis.period_end.isoformat() if analysis.period_end else None,
        "total_users": analysis.total_users,
        "step_conversions": analysis.step_conversions,
        "overall_conversion": analysis.overall_conversion,
        "drop_off_points": analysis.drop_off_points,
    }


@app.get("/api/v1/analytics/retention/{tenant_id}", tags=["Growth & Analytics"])
async def calculate_retention(
    tenant_id: str,
    cohort_date: str,
    periods: str | None = None,  # JSON array: [1, 3, 7, 14, 30]
):
    """计算留存率"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    cohort = datetime.fromisoformat(cohort_date)
    period_list = json.loads(periods) if periods else [1, 3, 7, 14, 30]

    retention = manager.calculate_retention(tenant_id, cohort, period_list)

    return retention


# ==================== A/B 测试 API ====================


@app.post("/api/v1/experiments", tags=["Growth & Analytics"])
async def create_experiment_endpoint(request: CreateExperimentRequest, created_by: str = "system"):
    """创建 A/B 测试实验"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    tenant_id = "default_tenant"  # Should come from auth context

    try:
        experiment = manager.create_experiment(
            tenant_id=tenant_id,
            name=request.name,
            description=request.description,
            hypothesis=request.hypothesis,
            variants=request.variants,
            traffic_allocation=TrafficAllocationType(request.traffic_allocation),
            traffic_split=request.traffic_split,
            target_audience=request.target_audience,
            primary_metric=request.primary_metric,
            secondary_metrics=request.secondary_metrics,
            min_sample_size=request.min_sample_size,
            confidence_level=request.confidence_level,
            created_by=created_by,
        )

        return {
            "id": experiment.id,
            "name": experiment.name,
            "status": experiment.status.value,
            "variants": experiment.variants,
            "created_at": experiment.created_at,
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/experiments", tags=["Growth & Analytics"])
async def list_experiments(status: str | None = None):
    """列出实验"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    exp_status = ExperimentStatus(status) if status else None
    experiments = manager.list_experiments(tenant_id, exp_status)

    return {
        "experiments": [
            {
                "id": e.id,
                "name": e.name,
                "status": e.status.value,
                "hypothesis": e.hypothesis,
                "primary_metric": e.primary_metric,
                "start_date": e.start_date.isoformat() if e.start_date else None,
                "end_date": e.end_date.isoformat() if e.end_date else None,
            }
            for e in experiments
        ]
    }


@app.get("/api/v1/experiments/{experiment_id}", tags=["Growth & Analytics"])
async def get_experiment_endpoint(experiment_id: str):
    """获取实验详情"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    experiment = manager.get_experiment(experiment_id)

    if not experiment:
        raise HTTPException(status_code=404, detail="Experiment not found")

    return {
        "id": experiment.id,
        "name": experiment.name,
        "description": experiment.description,
        "hypothesis": experiment.hypothesis,
        "status": experiment.status.value,
        "variants": experiment.variants,
        "traffic_allocation": experiment.traffic_allocation.value,
        "primary_metric": experiment.primary_metric,
        "secondary_metrics": experiment.secondary_metrics,
        "start_date": experiment.start_date.isoformat() if experiment.start_date else None,
        "end_date": experiment.end_date.isoformat() if experiment.end_date else None,
    }


@app.post("/api/v1/experiments/{experiment_id}/assign", tags=["Growth & Analytics"])
async def assign_variant_endpoint(experiment_id: str, request: AssignVariantRequest):
    """为用户分配实验变体"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    variant_id = manager.assign_variant(
        experiment_id=experiment_id,
        user_id=request.user_id,
        user_attributes=request.user_attributes,
    )

    if not variant_id:
        raise HTTPException(status_code=400, detail="Failed to assign variant")

    return {"experiment_id": experiment_id, "user_id": request.user_id, "variant_id": variant_id}


@app.post("/api/v1/experiments/{experiment_id}/metrics", tags=["Growth & Analytics"])
async def record_experiment_metric_endpoint(experiment_id: str, request: RecordMetricRequest):
    """记录实验指标"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    manager.record_experiment_metric(
        experiment_id=experiment_id,
        variant_id=request.variant_id,
        user_id=request.user_id,
        metric_name=request.metric_name,
        metric_value=request.metric_value,
    )

    return {"success": True}


@app.get("/api/v1/experiments/{experiment_id}/analyze", tags=["Growth & Analytics"])
async def analyze_experiment_endpoint(experiment_id: str):
    """分析实验结果"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    result = manager.analyze_experiment(experiment_id)

    if "error" in result:
        raise HTTPException(status_code=404, detail=result["error"])

    return result


@app.post("/api/v1/experiments/{experiment_id}/start", tags=["Growth & Analytics"])
async def start_experiment_endpoint(experiment_id: str):
    """启动实验"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    experiment = manager.start_experiment(experiment_id)

    if not experiment:
        raise HTTPException(status_code=404, detail="Experiment not found or not in draft status")

    return {
        "id": experiment.id,
        "status": experiment.status.value,
        "start_date": experiment.start_date.isoformat() if experiment.start_date else None,
    }


@app.post("/api/v1/experiments/{experiment_id}/stop", tags=["Growth & Analytics"])
async def stop_experiment_endpoint(experiment_id: str):
    """停止实验"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    experiment = manager.stop_experiment(experiment_id)

    if not experiment:
        raise HTTPException(status_code=404, detail="Experiment not found or not running")

    return {
        "id": experiment.id,
        "status": experiment.status.value,
        "end_date": experiment.end_date.isoformat() if experiment.end_date else None,
    }


# ==================== 邮件营销 API ====================


@app.post("/api/v1/email/templates", tags=["Growth & Analytics"])
async def create_email_template_endpoint(request: CreateEmailTemplateRequest):
    """创建邮件模板"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    try:
        template = manager.create_email_template(
            tenant_id=tenant_id,
            name=request.name,
            template_type=EmailTemplateType(request.template_type),
            subject=request.subject,
            html_content=request.html_content,
            text_content=request.text_content,
            variables=request.variables,
            from_name=request.from_name,
            from_email=request.from_email,
            reply_to=request.reply_to,
        )

        return {
            "id": template.id,
            "name": template.name,
            "template_type": template.template_type.value,
            "subject": template.subject,
            "variables": template.variables,
            "created_at": template.created_at,
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/email/templates", tags=["Growth & Analytics"])
async def list_email_templates(template_type: str | None = None):
    """列出邮件模板"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    t_type = EmailTemplateType(template_type) if template_type else None
    templates = manager.list_email_templates(tenant_id, t_type)

    return {
        "templates": [
            {
                "id": t.id,
                "name": t.name,
                "template_type": t.template_type.value,
                "subject": t.subject,
                "variables": t.variables,
                "is_active": t.is_active,
            }
            for t in templates
        ]
    }


@app.get("/api/v1/email/templates/{template_id}", tags=["Growth & Analytics"])
async def get_email_template_endpoint(template_id: str):
    """获取邮件模板详情"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    template = manager.get_email_template(template_id)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {
        "id": template.id,
        "name": template.name,
        "template_type": template.template_type.value,
        "subject": template.subject,
        "html_content": template.html_content,
        "text_content": template.text_content,
        "variables": template.variables,
        "from_name": template.from_name,
        "from_email": template.from_email,
    }


@app.post("/api/v1/email/templates/{template_id}/render", tags=["Growth & Analytics"])
async def render_template_endpoint(template_id: str, variables: dict):
    """渲染邮件模板"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    rendered = manager.render_template(template_id, variables)

    if not rendered:
        raise HTTPException(status_code=404, detail="Template not found")

    return rendered


@app.post("/api/v1/email/campaigns", tags=["Growth & Analytics"])
async def create_email_campaign_endpoint(request: CreateCampaignRequest):
    """创建邮件营销活动"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    scheduled_at = datetime.fromisoformat(request.scheduled_at) if request.scheduled_at else None

    campaign = manager.create_email_campaign(
        tenant_id=tenant_id,
        name=request.name,
        template_id=request.template_id,
        recipient_list=request.recipients,
        scheduled_at=scheduled_at,
    )

    return {
        "id": campaign.id,
        "name": campaign.name,
        "template_id": campaign.template_id,
        "status": campaign.status,
        "recipient_count": campaign.recipient_count,
        "scheduled_at": campaign.scheduled_at,
    }


@app.post("/api/v1/email/campaigns/{campaign_id}/send", tags=["Growth & Analytics"])
async def send_campaign_endpoint(campaign_id: str):
    """发送邮件营销活动"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    result = await manager.send_campaign(campaign_id)

    if "error" in result:
        raise HTTPException(status_code=404, detail=result["error"])

    return result


@app.post("/api/v1/email/workflows", tags=["Growth & Analytics"])
async def create_automation_workflow_endpoint(request: CreateAutomationWorkflowRequest):
    """创建自动化工作流"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    workflow = manager.create_automation_workflow(
        tenant_id=tenant_id,
        name=request.name,
        description=request.description,
        trigger_type=WorkflowTriggerType(request.trigger_type),
        trigger_conditions=request.trigger_conditions,
        actions=request.actions,
    )

    return {
        "id": workflow.id,
        "name": workflow.name,
        "trigger_type": workflow.trigger_type.value,
        "is_active": workflow.is_active,
        "created_at": workflow.created_at,
    }


# ==================== 推荐系统 API ====================


@app.post("/api/v1/referral/programs", tags=["Growth & Analytics"])
async def create_referral_program_endpoint(request: CreateReferralProgramRequest):
    """创建推荐计划"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    program = manager.create_referral_program(
        tenant_id=tenant_id,
        name=request.name,
        description=request.description,
        referrer_reward_type=request.referrer_reward_type,
        referrer_reward_value=request.referrer_reward_value,
        referee_reward_type=request.referee_reward_type,
        referee_reward_value=request.referee_reward_value,
        max_referrals_per_user=request.max_referrals_per_user,
        referral_code_length=request.referral_code_length,
        expiry_days=request.expiry_days,
    )

    return {
        "id": program.id,
        "name": program.name,
        "referrer_reward_type": program.referrer_reward_type,
        "referrer_reward_value": program.referrer_reward_value,
        "referee_reward_type": program.referee_reward_type,
        "referee_reward_value": program.referee_reward_value,
        "is_active": program.is_active,
    }


@app.post("/api/v1/referral/programs/{program_id}/generate-code", tags=["Growth & Analytics"])
async def generate_referral_code_endpoint(program_id: str, referrer_id: str):
    """生成推荐码"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    referral = manager.generate_referral_code(program_id, referrer_id)

    if not referral:
        raise HTTPException(status_code=400, detail="Failed to generate referral code")

    return {
        "id": referral.id,
        "referral_code": referral.referral_code,
        "referrer_id": referral.referrer_id,
        "status": referral.status.value,
        "expires_at": referral.expires_at.isoformat(),
    }


@app.post("/api/v1/referral/apply", tags=["Growth & Analytics"])
async def apply_referral_code_endpoint(request: ApplyReferralCodeRequest):
    """应用推荐码"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    success = manager.apply_referral_code(request.referral_code, request.referee_id)

    if not success:
        raise HTTPException(status_code=400, detail="Invalid or expired referral code")

    return {"success": True, "message": "Referral code applied successfully"}


@app.get("/api/v1/referral/programs/{program_id}/stats", tags=["Growth & Analytics"])
async def get_referral_stats_endpoint(program_id: str):
    """获取推荐统计"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    stats = manager.get_referral_stats(program_id)

    return stats


@app.post("/api/v1/team-incentives", tags=["Growth & Analytics"])
async def create_team_incentive_endpoint(request: CreateTeamIncentiveRequest):
    """创建团队升级激励"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()
    tenant_id = "default_tenant"

    incentive = manager.create_team_incentive(
        tenant_id=tenant_id,
        name=request.name,
        description=request.description,
        target_tier=request.target_tier,
        min_team_size=request.min_team_size,
        incentive_type=request.incentive_type,
        incentive_value=request.incentive_value,
        valid_from=datetime.fromisoformat(request.valid_from),
        valid_until=datetime.fromisoformat(request.valid_until),
    )

    return {
        "id": incentive.id,
        "name": incentive.name,
        "target_tier": incentive.target_tier,
        "min_team_size": incentive.min_team_size,
        "incentive_type": incentive.incentive_type,
        "incentive_value": incentive.incentive_value,
        "valid_from": incentive.valid_from.isoformat(),
        "valid_until": incentive.valid_until.isoformat(),
    }


@app.get("/api/v1/team-incentives/check", tags=["Growth & Analytics"])
async def check_team_incentive_eligibility(tenant_id: str, current_tier: str, team_size: int):
    """检查团队激励资格"""
    if not GROWTH_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Growth manager not available")

    manager = get_growth_manager_instance()

    incentives = manager.check_team_incentive_eligibility(tenant_id, current_tier, team_size)

    return {
        "eligible_incentives": [
            {
                "id": i.id,
                "name": i.name,
                "incentive_type": i.incentive_type,
                "incentive_value": i.incentive_value,
            }
            for i in incentives
        ]
    }


# Serve frontend - MUST be last to not override API routes

# ============================================
# Phase 8 Task 6: Developer Ecosystem API
# ============================================

# Phase 8: Developer Ecosystem Manager
try:
    from developer_ecosystem_manager import (
        DeveloperEcosystemManager,
        DeveloperStatus,
        PluginCategory,
        SDKLanguage,
        SDKStatus,
        TemplateCategory,
        TemplateStatus,
    )

    DEVELOPER_ECOSYSTEM_AVAILABLE = True
except ImportError as e:
    print(f"Developer Ecosystem Manager import error: {e}")
    DEVELOPER_ECOSYSTEM_AVAILABLE = False


# Pydantic Models for Developer Ecosystem API
class SDKReleaseCreate(BaseModel):
    name: str
    language: str
    version: str
    description: str
    changelog: str = ""
    download_url: str
    documentation_url: str = ""
    repository_url: str = ""
    package_name: str
    min_platform_version: str = "1.0.0"
    dependencies: list[dict] = Field(default_factory=list)
    file_size: int = 0
    checksum: str = ""


class SDKReleaseUpdate(BaseModel):
    name: str | None = None
    description: str | None = None
    changelog: str | None = None
    download_url: str | None = None
    documentation_url: str | None = None
    repository_url: str | None = None
    status: str | None = None


class SDKVersionCreate(BaseModel):
    version: str
    is_lts: bool = False
    release_notes: str = ""
    download_url: str
    checksum: str = ""
    file_size: int = 0


class TemplateCreate(BaseModel):
    name: str
    description: str
    category: str
    subcategory: str | None = None
    tags: list[str] = Field(default_factory=list)
    price: float = 0.0
    currency: str = "CNY"
    preview_image_url: str | None = None
    demo_url: str | None = None
    documentation_url: str | None = None
    download_url: str | None = None
    version: str = "1.0.0"
    min_platform_version: str = "1.0.0"
    file_size: int = 0
    checksum: str = ""


class TemplateReviewCreate(BaseModel):
    rating: int = Field(..., ge=1, le=5)
    comment: str = ""
    is_verified_purchase: bool = False


class PluginCreate(BaseModel):
    name: str
    description: str
    category: str
    tags: list[str] = Field(default_factory=list)
    price: float = 0.0
    currency: str = "CNY"
    pricing_model: str = "free"
    preview_image_url: str | None = None
    demo_url: str | None = None
    documentation_url: str | None = None
    repository_url: str | None = None
    download_url: str | None = None
    webhook_url: str | None = None
    permissions: list[str] = Field(default_factory=list)
    version: str = "1.0.0"
    min_platform_version: str = "1.0.0"
    file_size: int = 0
    checksum: str = ""


class PluginReviewCreate(BaseModel):
    rating: int = Field(..., ge=1, le=5)
    comment: str = ""
    is_verified_purchase: bool = False


class DeveloperProfileCreate(BaseModel):
    display_name: str
    email: str
    bio: str | None = None
    website: str | None = None
    github_url: str | None = None
    avatar_url: str | None = None


class DeveloperProfileUpdate(BaseModel):
    display_name: str | None = None
    bio: str | None = None
    website: str | None = None
    github_url: str | None = None
    avatar_url: str | None = None


class CodeExampleCreate(BaseModel):
    title: str
    description: str = ""
    language: str
    category: str
    code: str
    explanation: str = ""
    tags: list[str] = Field(default_factory=list)
    sdk_id: str | None = None
    api_endpoints: list[str] = Field(default_factory=list)


class PortalConfigCreate(BaseModel):
    name: str
    description: str = ""
    theme: str = "default"
    custom_css: str | None = None
    custom_js: str | None = None
    logo_url: str | None = None
    favicon_url: str | None = None
    primary_color: str = "#1890ff"
    secondary_color: str = "#52c41a"
    support_email: str = "support@insightflow.io"
    support_url: str | None = None
    github_url: str | None = None
    discord_url: str | None = None
    api_base_url: str = "https://api.insightflow.io"


# Developer Ecosystem Manager singleton
_developer_ecosystem_manager = None


def get_developer_ecosystem_manager_instance():
    global _developer_ecosystem_manager
    if _developer_ecosystem_manager is None and DEVELOPER_ECOSYSTEM_AVAILABLE:
        _developer_ecosystem_manager = DeveloperEcosystemManager()
    return _developer_ecosystem_manager


# ==================== SDK Release & Management API ====================


@app.post("/api/v1/developer/sdks", tags=["Developer Ecosystem"])
async def create_sdk_release_endpoint(
    request: SDKReleaseCreate, created_by: str = Header(default="system", description="创建者ID")
):
    """创建 SDK 发布"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    try:
        sdk = manager.create_sdk_release(
            name=request.name,
            language=SDKLanguage(request.language),
            version=request.version,
            description=request.description,
            changelog=request.changelog,
            download_url=request.download_url,
            documentation_url=request.documentation_url,
            repository_url=request.repository_url,
            package_name=request.package_name,
            min_platform_version=request.min_platform_version,
            dependencies=request.dependencies,
            file_size=request.file_size,
            checksum=request.checksum,
            created_by=created_by,
        )

        return {
            "id": sdk.id,
            "name": sdk.name,
            "language": sdk.language.value,
            "version": sdk.version,
            "status": sdk.status.value,
            "package_name": sdk.package_name,
            "created_at": sdk.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/developer/sdks", tags=["Developer Ecosystem"])
async def list_sdk_releases_endpoint(
    language: str | None = Query(default=None, description="SDK语言过滤"),
    status: str | None = Query(default=None, description="状态过滤"),
    search: str | None = Query(default=None, description="搜索关键词"),
):
    """列出 SDK 发布"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    language_enum = SDKLanguage(language) if language else None
    status_enum = SDKStatus(status) if status else None

    sdks = manager.list_sdk_releases(language_enum, status_enum, search)

    return {
        "sdks": [
            {
                "id": s.id,
                "name": s.name,
                "language": s.language.value,
                "version": s.version,
                "description": s.description,
                "package_name": s.package_name,
                "status": s.status.value,
                "download_count": s.download_count,
                "created_at": s.created_at,
            }
            for s in sdks
        ]
    }


@app.get("/api/v1/developer/sdks/{sdk_id}", tags=["Developer Ecosystem"])
async def get_sdk_release_endpoint(sdk_id: str):
    """获取 SDK 发布详情"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    sdk = manager.get_sdk_release(sdk_id)

    if not sdk:
        raise HTTPException(status_code=404, detail="SDK not found")

    return {
        "id": sdk.id,
        "name": sdk.name,
        "language": sdk.language.value,
        "version": sdk.version,
        "description": sdk.description,
        "changelog": sdk.changelog,
        "download_url": sdk.download_url,
        "documentation_url": sdk.documentation_url,
        "repository_url": sdk.repository_url,
        "package_name": sdk.package_name,
        "status": sdk.status.value,
        "min_platform_version": sdk.min_platform_version,
        "dependencies": sdk.dependencies,
        "file_size": sdk.file_size,
        "checksum": sdk.checksum,
        "download_count": sdk.download_count,
        "created_at": sdk.created_at,
        "published_at": sdk.published_at,
    }


@app.put("/api/v1/developer/sdks/{sdk_id}", tags=["Developer Ecosystem"])
async def update_sdk_release_endpoint(sdk_id: str, request: SDKReleaseUpdate):
    """更新 SDK 发布"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    update_data = {k: v for k, v in request.dict().items() if v is not None}
    sdk = manager.update_sdk_release(sdk_id, **update_data)

    if not sdk:
        raise HTTPException(status_code=404, detail="SDK not found")

    return {
        "id": sdk.id,
        "name": sdk.name,
        "status": sdk.status.value,
        "updated_at": sdk.updated_at,
    }


@app.post("/api/v1/developer/sdks/{sdk_id}/publish", tags=["Developer Ecosystem"])
async def publish_sdk_release_endpoint(sdk_id: str):
    """发布 SDK"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    sdk = manager.publish_sdk_release(sdk_id)

    if not sdk:
        raise HTTPException(status_code=404, detail="SDK not found")

    return {"id": sdk.id, "status": sdk.status.value, "published_at": sdk.published_at}


@app.post("/api/v1/developer/sdks/{sdk_id}/download", tags=["Developer Ecosystem"])
async def increment_sdk_download_endpoint(sdk_id: str):
    """记录 SDK 下载"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    manager.increment_sdk_download(sdk_id)

    return {"success": True, "message": "Download counted"}


@app.get("/api/v1/developer/sdks/{sdk_id}/versions", tags=["Developer Ecosystem"])
async def get_sdk_versions_endpoint(sdk_id: str):
    """获取 SDK 版本历史"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    versions = manager.get_sdk_versions(sdk_id)

    return {
        "versions": [
            {
                "id": v.id,
                "version": v.version,
                "is_latest": v.is_latest,
                "is_lts": v.is_lts,
                "download_count": v.download_count,
                "created_at": v.created_at,
            }
            for v in versions
        ]
    }


@app.post("/api/v1/developer/sdks/{sdk_id}/versions", tags=["Developer Ecosystem"])
async def add_sdk_version_endpoint(sdk_id: str, request: SDKVersionCreate):
    """添加 SDK 版本"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    version = manager.add_sdk_version(
        sdk_id=sdk_id,
        version=request.version,
        is_lts=request.is_lts,
        release_notes=request.release_notes,
        download_url=request.download_url,
        checksum=request.checksum,
        file_size=request.file_size,
    )

    return {
        "id": version.id,
        "version": version.version,
        "is_latest": version.is_latest,
        "is_lts": version.is_lts,
        "created_at": version.created_at,
    }


# ==================== Template Market API ====================


@app.post("/api/v1/developer/templates", tags=["Developer Ecosystem"])
async def create_template_endpoint(
    request: TemplateCreate,
    author_id: str = Header(default="system", description="作者ID"),
    author_name: str = Header(default="System", description="作者名称"),
):
    """创建模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    try:
        template = manager.create_template(
            name=request.name,
            description=request.description,
            category=TemplateCategory(request.category),
            subcategory=request.subcategory,
            tags=request.tags,
            author_id=author_id,
            author_name=author_name,
            price=request.price,
            currency=request.currency,
            preview_image_url=request.preview_image_url,
            demo_url=request.demo_url,
            documentation_url=request.documentation_url,
            download_url=request.download_url,
            version=request.version,
            min_platform_version=request.min_platform_version,
            file_size=request.file_size,
            checksum=request.checksum,
        )

        return {
            "id": template.id,
            "name": template.name,
            "category": template.category.value,
            "status": template.status.value,
            "price": template.price,
            "created_at": template.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/developer/templates", tags=["Developer Ecosystem"])
async def list_templates_endpoint(
    category: str | None = Query(default=None, description="分类过滤"),
    status: str | None = Query(default=None, description="状态过滤"),
    search: str | None = Query(default=None, description="搜索关键词"),
    author_id: str | None = Query(default=None, description="作者ID过滤"),
    min_price: float | None = Query(default=None, description="最低价格"),
    max_price: float | None = Query(default=None, description="最高价格"),
    sort_by: str = Query(default="created_at", description="排序方式"),
):
    """列出模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    category_enum = TemplateCategory(category) if category else None
    status_enum = TemplateStatus(status) if status else None

    templates = manager.list_templates(
        category=category_enum,
        status=status_enum,
        search=search,
        author_id=author_id,
        min_price=min_price,
        max_price=max_price,
        sort_by=sort_by,
    )

    return {
        "templates": [
            {
                "id": t.id,
                "name": t.name,
                "description": t.description,
                "category": t.category.value,
                "author_name": t.author_name,
                "status": t.status.value,
                "price": t.price,
                "currency": t.currency,
                "rating": t.rating,
                "install_count": t.install_count,
                "version": t.version,
                "created_at": t.created_at,
            }
            for t in templates
        ]
    }


@app.get("/api/v1/developer/templates/{template_id}", tags=["Developer Ecosystem"])
async def get_template_endpoint(template_id: str):
    """获取模板详情"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    template = manager.get_template(template_id)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {
        "id": template.id,
        "name": template.name,
        "description": template.description,
        "category": template.category.value,
        "subcategory": template.subcategory,
        "tags": template.tags,
        "author_id": template.author_id,
        "author_name": template.author_name,
        "status": template.status.value,
        "price": template.price,
        "currency": template.currency,
        "preview_image_url": template.preview_image_url,
        "demo_url": template.demo_url,
        "documentation_url": template.documentation_url,
        "download_url": template.download_url,
        "install_count": template.install_count,
        "rating": template.rating,
        "rating_count": template.rating_count,
        "review_count": template.review_count,
        "version": template.version,
        "created_at": template.created_at,
    }


@app.post("/api/v1/developer/templates/{template_id}/approve", tags=["Developer Ecosystem"])
async def approve_template_endpoint(template_id: str, reviewed_by: str = Header(default="system")):
    """审核通过模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    template = manager.approve_template(template_id, reviewed_by)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {"id": template.id, "status": template.status.value}


@app.post("/api/v1/developer/templates/{template_id}/publish", tags=["Developer Ecosystem"])
async def publish_template_endpoint(template_id: str):
    """发布模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    template = manager.publish_template(template_id)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {
        "id": template.id,
        "status": template.status.value,
        "published_at": template.published_at,
    }


@app.post("/api/v1/developer/templates/{template_id}/reject", tags=["Developer Ecosystem"])
async def reject_template_endpoint(template_id: str, reason: str = ""):
    """拒绝模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    template = manager.reject_template(template_id, reason)

    if not template:
        raise HTTPException(status_code=404, detail="Template not found")

    return {"id": template.id, "status": template.status.value}


@app.post("/api/v1/developer/templates/{template_id}/install", tags=["Developer Ecosystem"])
async def install_template_endpoint(template_id: str):
    """安装模板"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    manager.increment_template_install(template_id)

    return {"success": True, "message": "Template installed"}


@app.post("/api/v1/developer/templates/{template_id}/reviews", tags=["Developer Ecosystem"])
async def add_template_review_endpoint(
    template_id: str,
    request: TemplateReviewCreate,
    user_id: str = Header(default="user", description="用户ID"),
    user_name: str = Header(default="User", description="用户名称"),
):
    """添加模板评价"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    review = manager.add_template_review(
        template_id=template_id,
        user_id=user_id,
        user_name=user_name,
        rating=request.rating,
        comment=request.comment,
        is_verified_purchase=request.is_verified_purchase,
    )

    return {
        "id": review.id,
        "rating": review.rating,
        "comment": review.comment,
        "created_at": review.created_at,
    }


@app.get("/api/v1/developer/templates/{template_id}/reviews", tags=["Developer Ecosystem"])
async def get_template_reviews_endpoint(
    template_id: str, limit: int = Query(default=50, description="返回数量限制")
):
    """获取模板评价"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    reviews = manager.get_template_reviews(template_id, limit)

    return {
        "reviews": [
            {
                "id": r.id,
                "user_name": r.user_name,
                "rating": r.rating,
                "comment": r.comment,
                "is_verified_purchase": r.is_verified_purchase,
                "helpful_count": r.helpful_count,
                "created_at": r.created_at,
            }
            for r in reviews
        ]
    }


# ==================== Plugin Market API ====================


@app.post("/api/v1/developer/plugins", tags=["Developer Ecosystem"])
async def create_developer_plugin_endpoint(
    request: PluginCreate,
    author_id: str = Header(default="system", description="作者ID"),
    author_name: str = Header(default="System", description="作者名称"),
):
    """创建插件"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    try:
        plugin = manager.create_plugin(
            name=request.name,
            description=request.description,
            category=PluginCategory(request.category),
            tags=request.tags,
            author_id=author_id,
            author_name=author_name,
            price=request.price,
            currency=request.currency,
            pricing_model=request.pricing_model,
            preview_image_url=request.preview_image_url,
            demo_url=request.demo_url,
            documentation_url=request.documentation_url,
            repository_url=request.repository_url,
            download_url=request.download_url,
            webhook_url=request.webhook_url,
            permissions=request.permissions,
            version=request.version,
            min_platform_version=request.min_platform_version,
            file_size=request.file_size,
            checksum=request.checksum,
        )

        return {
            "id": plugin.id,
            "name": plugin.name,
            "category": plugin.category.value,
            "status": plugin.status.value,
            "price": plugin.price,
            "pricing_model": plugin.pricing_model,
            "created_at": plugin.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/developer/plugins", tags=["Developer Ecosystem"])
async def list_developer_plugins_endpoint(
    category: str | None = Query(default=None, description="分类过滤"),
    status: str | None = Query(default=None, description="状态过滤"),
    search: str | None = Query(default=None, description="搜索关键词"),
    author_id: str | None = Query(default=None, description="作者ID过滤"),
    sort_by: str = Query(default="created_at", description="排序方式"),
):
    """列出插件"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    category_enum = PluginCategory(category) if category else None
    status_enum = PluginStatus(status) if status else None

    plugins = manager.list_plugins(
        category=category_enum,
        status=status_enum,
        search=search,
        author_id=author_id,
        sort_by=sort_by,
    )

    return {
        "plugins": [
            {
                "id": p.id,
                "name": p.name,
                "description": p.description,
                "category": p.category.value,
                "author_name": p.author_name,
                "status": p.status.value,
                "price": p.price,
                "pricing_model": p.pricing_model,
                "rating": p.rating,
                "install_count": p.install_count,
                "active_install_count": p.active_install_count,
                "version": p.version,
                "created_at": p.created_at,
            }
            for p in plugins
        ]
    }


@app.get("/api/v1/developer/plugins/{plugin_id}", tags=["Developer Ecosystem"])
async def get_developer_plugin_endpoint(plugin_id: str):
    """获取插件详情"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    plugin = manager.get_plugin(plugin_id)

    if not plugin:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return {
        "id": plugin.id,
        "name": plugin.name,
        "description": plugin.description,
        "category": plugin.category.value,
        "tags": plugin.tags,
        "author_id": plugin.author_id,
        "author_name": plugin.author_name,
        "status": plugin.status.value,
        "price": plugin.price,
        "currency": plugin.currency,
        "pricing_model": plugin.pricing_model,
        "preview_image_url": plugin.preview_image_url,
        "demo_url": plugin.demo_url,
        "documentation_url": plugin.documentation_url,
        "repository_url": plugin.repository_url,
        "download_url": plugin.download_url,
        "permissions": plugin.permissions,
        "install_count": plugin.install_count,
        "active_install_count": plugin.active_install_count,
        "rating": plugin.rating,
        "version": plugin.version,
        "reviewed_by": plugin.reviewed_by,
        "reviewed_at": plugin.reviewed_at,
        "created_at": plugin.created_at,
    }


@app.post("/api/v1/developer/plugins/{plugin_id}/review", tags=["Developer Ecosystem"])
async def review_plugin_endpoint(
    plugin_id: str,
    status: str = Query(..., description="审核状态: approved/rejected"),
    reviewed_by: str = Header(default="system", description="审核人ID"),
    notes: str = "",
):
    """审核插件"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    try:
        status_enum = PluginStatus(status)
        plugin = manager.review_plugin(plugin_id, reviewed_by, status_enum, notes)

        if not plugin:
            raise HTTPException(status_code=404, detail="Plugin not found")

        return {
            "id": plugin.id,
            "status": plugin.status.value,
            "reviewed_by": plugin.reviewed_by,
            "reviewed_at": plugin.reviewed_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/developer/plugins/{plugin_id}/publish", tags=["Developer Ecosystem"])
async def publish_plugin_endpoint(plugin_id: str):
    """发布插件"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    plugin = manager.publish_plugin(plugin_id)

    if not plugin:
        raise HTTPException(status_code=404, detail="Plugin not found")

    return {"id": plugin.id, "status": plugin.status.value, "published_at": plugin.published_at}


@app.post("/api/v1/developer/plugins/{plugin_id}/install", tags=["Developer Ecosystem"])
async def install_plugin_endpoint(plugin_id: str, active: bool = True):
    """安装插件"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    manager.increment_plugin_install(plugin_id, active)

    return {"success": True, "message": "Plugin installed"}


@app.post("/api/v1/developer/plugins/{plugin_id}/reviews", tags=["Developer Ecosystem"])
async def add_plugin_review_endpoint(
    plugin_id: str,
    request: PluginReviewCreate,
    user_id: str = Header(default="user", description="用户ID"),
    user_name: str = Header(default="User", description="用户名称"),
):
    """添加插件评价"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    review = manager.add_plugin_review(
        plugin_id=plugin_id,
        user_id=user_id,
        user_name=user_name,
        rating=request.rating,
        comment=request.comment,
        is_verified_purchase=request.is_verified_purchase,
    )

    return {
        "id": review.id,
        "rating": review.rating,
        "comment": review.comment,
        "created_at": review.created_at,
    }


@app.get("/api/v1/developer/plugins/{plugin_id}/reviews", tags=["Developer Ecosystem"])
async def get_plugin_reviews_endpoint(
    plugin_id: str, limit: int = Query(default=50, description="返回数量限制")
):
    """获取插件评价"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    reviews = manager.get_plugin_reviews(plugin_id, limit)

    return {
        "reviews": [
            {
                "id": r.id,
                "user_name": r.user_name,
                "rating": r.rating,
                "comment": r.comment,
                "is_verified_purchase": r.is_verified_purchase,
                "helpful_count": r.helpful_count,
                "created_at": r.created_at,
            }
            for r in reviews
        ]
    }


# ==================== Developer Revenue Sharing API ====================


@app.get("/api/v1/developer/revenues/{developer_id}", tags=["Developer Ecosystem"])
async def get_developer_revenues_endpoint(
    developer_id: str,
    start_date: str | None = Query(default=None, description="开始日期 (ISO格式)"),
    end_date: str | None = Query(default=None, description="结束日期 (ISO格式)"),
):
    """获取开发者收益记录"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    start = datetime.fromisoformat(start_date) if start_date else None
    end = datetime.fromisoformat(end_date) if end_date else None

    revenues = manager.get_developer_revenues(developer_id, start, end)

    return {
        "revenues": [
            {
                "id": r.id,
                "item_type": r.item_type,
                "item_name": r.item_name,
                "sale_amount": r.sale_amount,
                "platform_fee": r.platform_fee,
                "developer_earnings": r.developer_earnings,
                "currency": r.currency,
                "created_at": r.created_at,
            }
            for r in revenues
        ]
    }


@app.get("/api/v1/developer/revenues/{developer_id}/summary", tags=["Developer Ecosystem"])
async def get_developer_revenue_summary_endpoint(developer_id: str):
    """获取开发者收益汇总"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    summary = manager.get_developer_revenue_summary(developer_id)

    return summary


# ==================== Developer Profile & Management API ====================


@app.post("/api/v1/developer/profiles", tags=["Developer Ecosystem"])
async def create_developer_profile_endpoint(request: DeveloperProfileCreate):
    """创建开发者档案"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    user_id = f"user_{uuid.uuid4().hex[:8]}"

    profile = manager.create_developer_profile(
        user_id=user_id,
        display_name=request.display_name,
        email=request.email,
        bio=request.bio,
        website=request.website,
        github_url=request.github_url,
        avatar_url=request.avatar_url,
    )

    return {
        "id": profile.id,
        "user_id": profile.user_id,
        "display_name": profile.display_name,
        "email": profile.email,
        "status": profile.status.value,
        "created_at": profile.created_at,
    }


@app.get("/api/v1/developer/profiles/{developer_id}", tags=["Developer Ecosystem"])
async def get_developer_profile_endpoint(developer_id: str):
    """获取开发者档案"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    profile = manager.get_developer_profile(developer_id)

    if not profile:
        raise HTTPException(status_code=404, detail="Developer profile not found")

    return {
        "id": profile.id,
        "user_id": profile.user_id,
        "display_name": profile.display_name,
        "email": profile.email,
        "bio": profile.bio,
        "website": profile.website,
        "github_url": profile.github_url,
        "avatar_url": profile.avatar_url,
        "status": profile.status.value,
        "total_sales": profile.total_sales,
        "total_downloads": profile.total_downloads,
        "plugin_count": profile.plugin_count,
        "template_count": profile.template_count,
        "rating_average": profile.rating_average,
        "created_at": profile.created_at,
        "verified_at": profile.verified_at,
    }


@app.get("/api/v1/developer/profiles/user/{user_id}", tags=["Developer Ecosystem"])
async def get_developer_profile_by_user_endpoint(user_id: str):
    """通过用户ID获取开发者档案"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    profile = manager.get_developer_profile_by_user(user_id)

    if not profile:
        raise HTTPException(status_code=404, detail="Developer profile not found")

    return {
        "id": profile.id,
        "user_id": profile.user_id,
        "display_name": profile.display_name,
        "status": profile.status.value,
        "total_sales": profile.total_sales,
        "total_downloads": profile.total_downloads,
    }


@app.put("/api/v1/developer/profiles/{developer_id}", tags=["Developer Ecosystem"])
async def update_developer_profile_endpoint(developer_id: str, request: DeveloperProfileUpdate):
    """更新开发者档案"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    return {"message": "Profile update endpoint - to be implemented"}


@app.post("/api/v1/developer/profiles/{developer_id}/verify", tags=["Developer Ecosystem"])
async def verify_developer_endpoint(
    developer_id: str,
    status: str = Query(..., description="认证状态: verified/certified/suspended"),
):
    """验证开发者"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    try:
        status_enum = DeveloperStatus(status)
        profile = manager.verify_developer(developer_id, status_enum)

        if not profile:
            raise HTTPException(status_code=404, detail="Developer profile not found")

        return {
            "id": profile.id,
            "status": profile.status.value,
            "verified_at": profile.verified_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.post("/api/v1/developer/profiles/{developer_id}/update-stats", tags=["Developer Ecosystem"])
async def update_developer_stats_endpoint(developer_id: str):
    """更新开发者统计信息"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    manager.update_developer_stats(developer_id)

    return {"success": True, "message": "Developer stats updated"}


# ==================== Code Examples API ====================


@app.post("/api/v1/developer/code-examples", tags=["Developer Ecosystem"])
async def create_code_example_endpoint(
    request: CodeExampleCreate,
    author_id: str = Header(default="system", description="作者ID"),
    author_name: str = Header(default="System", description="作者名称"),
):
    """创建代码示例"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    example = manager.create_code_example(
        title=request.title,
        description=request.description,
        language=request.language,
        category=request.category,
        code=request.code,
        explanation=request.explanation,
        tags=request.tags,
        author_id=author_id,
        author_name=author_name,
        sdk_id=request.sdk_id,
        api_endpoints=request.api_endpoints,
    )

    return {
        "id": example.id,
        "title": example.title,
        "language": example.language,
        "category": example.category,
        "tags": example.tags,
        "created_at": example.created_at,
    }


@app.get("/api/v1/developer/code-examples", tags=["Developer Ecosystem"])
async def list_code_examples_endpoint(
    language: str | None = Query(default=None, description="编程语言过滤"),
    category: str | None = Query(default=None, description="分类过滤"),
    sdk_id: str | None = Query(default=None, description="SDK ID过滤"),
    search: str | None = Query(default=None, description="搜索关键词"),
):
    """列出代码示例"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    examples = manager.list_code_examples(language, category, sdk_id, search)

    return {
        "examples": [
            {
                "id": e.id,
                "title": e.title,
                "description": e.description,
                "language": e.language,
                "category": e.category,
                "tags": e.tags,
                "author_name": e.author_name,
                "view_count": e.view_count,
                "copy_count": e.copy_count,
                "rating": e.rating,
                "created_at": e.created_at,
            }
            for e in examples
        ]
    }


@app.get("/api/v1/developer/code-examples/{example_id}", tags=["Developer Ecosystem"])
async def get_code_example_endpoint(example_id: str):
    """获取代码示例详情"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    example = manager.get_code_example(example_id)

    if not example:
        raise HTTPException(status_code=404, detail="Code example not found")

    manager.increment_example_view(example_id)

    return {
        "id": example.id,
        "title": example.title,
        "description": example.description,
        "language": example.language,
        "category": example.category,
        "code": example.code,
        "explanation": example.explanation,
        "tags": example.tags,
        "author_name": example.author_name,
        "sdk_id": example.sdk_id,
        "api_endpoints": example.api_endpoints,
        "view_count": example.view_count,
        "copy_count": example.copy_count,
        "rating": example.rating,
        "created_at": example.created_at,
    }


@app.post("/api/v1/developer/code-examples/{example_id}/copy", tags=["Developer Ecosystem"])
async def copy_code_example_endpoint(example_id: str):
    """复制代码示例"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    manager.increment_example_copy(example_id)

    return {"success": True, "message": "Code copied"}


# ==================== API Documentation API ====================


@app.get("/api/v1/developer/api-docs", tags=["Developer Ecosystem"])
async def get_latest_api_documentation_endpoint():
    """获取最新 API 文档"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    doc = manager.get_latest_api_documentation()

    if not doc:
        raise HTTPException(status_code=404, detail="API documentation not found")

    return {
        "id": doc.id,
        "version": doc.version,
        "changelog": doc.changelog,
        "generated_at": doc.generated_at,
        "generated_by": doc.generated_by,
    }


@app.get("/api/v1/developer/api-docs/{doc_id}", tags=["Developer Ecosystem"])
async def get_api_documentation_endpoint(doc_id: str):
    """获取 API 文档详情"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    doc = manager.get_api_documentation(doc_id)

    if not doc:
        raise HTTPException(status_code=404, detail="API documentation not found")

    return {
        "id": doc.id,
        "version": doc.version,
        "openapi_spec": doc.openapi_spec,
        "markdown_content": doc.markdown_content,
        "html_content": doc.html_content,
        "changelog": doc.changelog,
        "generated_at": doc.generated_at,
        "generated_by": doc.generated_by,
    }


# ==================== Developer Portal API ====================


@app.post("/api/v1/developer/portal-configs", tags=["Developer Ecosystem"])
async def create_portal_config_endpoint(request: PortalConfigCreate):
    """创建开发者门户配置"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()

    config = manager.create_portal_config(
        name=request.name,
        description=request.description,
        theme=request.theme,
        custom_css=request.custom_css,
        custom_js=request.custom_js,
        logo_url=request.logo_url,
        favicon_url=request.favicon_url,
        primary_color=request.primary_color,
        secondary_color=request.secondary_color,
        support_email=request.support_email,
        support_url=request.support_url,
        github_url=request.github_url,
        discord_url=request.discord_url,
        api_base_url=request.api_base_url,
    )

    return {
        "id": config.id,
        "name": config.name,
        "theme": config.theme,
        "is_active": config.is_active,
        "created_at": config.created_at,
    }


@app.get("/api/v1/developer/portal-configs", tags=["Developer Ecosystem"])
async def get_active_portal_config_endpoint():
    """获取活跃的开发者门户配置"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    config = manager.get_active_portal_config()

    if not config:
        raise HTTPException(status_code=404, detail="Portal config not found")

    return {
        "id": config.id,
        "name": config.name,
        "description": config.description,
        "theme": config.theme,
        "logo_url": config.logo_url,
        "favicon_url": config.favicon_url,
        "primary_color": config.primary_color,
        "secondary_color": config.secondary_color,
        "support_email": config.support_email,
        "support_url": config.support_url,
        "github_url": config.github_url,
        "discord_url": config.discord_url,
        "api_base_url": config.api_base_url,
        "is_active": config.is_active,
    }


@app.get("/api/v1/developer/portal-configs/{config_id}", tags=["Developer Ecosystem"])
async def get_portal_config_endpoint(config_id: str):
    """获取开发者门户配置"""
    if not DEVELOPER_ECOSYSTEM_AVAILABLE:
        raise HTTPException(status_code=503, detail="Developer ecosystem manager not available")

    manager = get_developer_ecosystem_manager_instance()
    config = manager.get_portal_config(config_id)

    if not config:
        raise HTTPException(status_code=404, detail="Portal config not found")

    return {
        "id": config.id,
        "name": config.name,
        "description": config.description,
        "theme": config.theme,
        "primary_color": config.primary_color,
        "secondary_color": config.secondary_color,
        "support_email": config.support_email,
        "api_base_url": config.api_base_url,
        "is_active": config.is_active,
    }


# ==================== Phase 8 Task 8: Operations & Monitoring Endpoints ====================

# Ops Manager singleton
_ops_manager = None


def get_ops_manager_instance():
    global _ops_manager
    if _ops_manager is None and OPS_MANAGER_AVAILABLE:
        _ops_manager = get_ops_manager()
    return _ops_manager


# Pydantic Models for Ops API
class AlertRuleCreate(BaseModel):
    name: str = Field(..., description="告警规则名称")
    description: str = Field(default="", description="告警规则描述")
    rule_type: str = Field(..., description="规则类型: threshold, anomaly, predictive, composite")
    severity: str = Field(..., description="告警级别: p0, p1, p2, p3")
    metric: str = Field(..., description="监控指标")
    condition: str = Field(..., description="条件: >, <, >=, <=, ==, !=")
    threshold: float = Field(..., description="阈值")
    duration: int = Field(default=60, description="持续时间（秒）")
    evaluation_interval: int = Field(default=60, description="评估间隔（秒）")
    channels: list[str] = Field(default_factory=list, description="告警渠道ID列表")
    labels: dict = Field(default_factory=dict, description="标签")
    annotations: dict = Field(default_factory=dict, description="注释")


class AlertRuleResponse(BaseModel):
    id: str
    name: str
    description: str
    rule_type: str
    severity: str
    metric: str
    condition: str
    threshold: float
    duration: int
    evaluation_interval: int
    channels: list[str]
    labels: dict
    annotations: dict
    is_enabled: bool
    created_at: str
    updated_at: str


class AlertChannelCreate(BaseModel):
    name: str = Field(..., description="渠道名称")
    channel_type: str = Field(
        ...,
        description="渠道类型: pagerduty, opsgenie, feishu, dingtalk, slack, email, sms, webhook",
    )
    config: dict = Field(default_factory=dict, description="渠道特定配置")
    severity_filter: list[str] = Field(
        default_factory=lambda: ["p0", "p1", "p2", "p3"], description="过滤的告警级别"
    )


class AlertChannelResponse(BaseModel):
    id: str
    name: str
    channel_type: str
    config: dict
    severity_filter: list[str]
    is_enabled: bool
    success_count: int
    fail_count: int
    last_used_at: str | None
    created_at: str


class AlertResponse(BaseModel):
    id: str
    rule_id: str
    severity: str
    status: str
    title: str
    description: str
    metric: str
    value: float
    threshold: float
    labels: dict
    started_at: str
    resolved_at: str | None
    acknowledged_by: str | None
    suppression_count: int


class HealthCheckCreate(BaseModel):
    name: str = Field(..., description="健康检查名称")
    target_type: str = Field(..., description="目标类型: service, database, api")
    target_id: str = Field(..., description="目标ID")
    check_type: str = Field(..., description="检查类型: http, tcp, ping, custom")
    check_config: dict = Field(default_factory=dict, description="检查配置")
    interval: int = Field(default=60, description="检查间隔（秒）")
    timeout: int = Field(default=10, description="超时时间（秒）")
    retry_count: int = Field(default=3, description="重试次数")


class HealthCheckResponse(BaseModel):
    id: str
    name: str
    target_type: str
    target_id: str
    check_type: str
    interval: int
    timeout: int
    is_enabled: bool
    created_at: str


class AutoScalingPolicyCreate(BaseModel):
    name: str = Field(..., description="策略名称")
    resource_type: str = Field(
        ..., description="资源类型: cpu, memory, disk, network, gpu, database, cache, queue"
    )
    min_instances: int = Field(default=1, description="最小实例数")
    max_instances: int = Field(default=10, description="最大实例数")
    target_utilization: float = Field(default=0.7, description="目标利用率")
    scale_up_threshold: float = Field(default=0.8, description="扩容阈值")
    scale_down_threshold: float = Field(default=0.3, description="缩容阈值")
    scale_up_step: int = Field(default=1, description="扩容步长")
    scale_down_step: int = Field(default=1, description="缩容步长")
    cooldown_period: int = Field(default=300, description="冷却时间（秒）")


class BackupJobCreate(BaseModel):
    name: str = Field(..., description="备份任务名称")
    backup_type: str = Field(..., description="备份类型: full, incremental, differential")
    target_type: str = Field(..., description="目标类型: database, files, configuration")
    target_id: str = Field(..., description="目标ID")
    schedule: str = Field(..., description="Cron 表达式")
    retention_days: int = Field(default=30, description="保留天数")
    encryption_enabled: bool = Field(default=True, description="是否加密")
    compression_enabled: bool = Field(default=True, description="是否压缩")
    storage_location: str | None = Field(default=None, description="存储位置")


# Alert Rules API
@app.post(
    "/api/v1/ops/alert-rules", response_model=AlertRuleResponse, tags=["Operations & Monitoring"]
)
async def create_alert_rule_endpoint(
    tenant_id: str, request: AlertRuleCreate, user_id: str = "system", _=Depends(verify_api_key)
):
    """创建告警规则"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    try:
        rule = manager.create_alert_rule(
            tenant_id=tenant_id,
            name=request.name,
            description=request.description,
            rule_type=AlertRuleType(request.rule_type),
            severity=AlertSeverity(request.severity),
            metric=request.metric,
            condition=request.condition,
            threshold=request.threshold,
            duration=request.duration,
            evaluation_interval=request.evaluation_interval,
            channels=request.channels,
            labels=request.labels,
            annotations=request.annotations,
            created_by=user_id,
        )

        return AlertRuleResponse(
            id=rule.id,
            name=rule.name,
            description=rule.description,
            rule_type=rule.rule_type.value,
            severity=rule.severity.value,
            metric=rule.metric,
            condition=rule.condition,
            threshold=rule.threshold,
            duration=rule.duration,
            evaluation_interval=rule.evaluation_interval,
            channels=rule.channels,
            labels=rule.labels,
            annotations=rule.annotations,
            is_enabled=rule.is_enabled,
            created_at=rule.created_at,
            updated_at=rule.updated_at,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ops/alert-rules", tags=["Operations & Monitoring"])
async def list_alert_rules_endpoint(
    tenant_id: str, is_enabled: bool | None = None, _=Depends(verify_api_key)
):
    """列出租户的告警规则"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    rules = manager.list_alert_rules(tenant_id, is_enabled=is_enabled)

    return [
        AlertRuleResponse(
            id=rule.id,
            name=rule.name,
            description=rule.description,
            rule_type=rule.rule_type.value,
            severity=rule.severity.value,
            metric=rule.metric,
            condition=rule.condition,
            threshold=rule.threshold,
            duration=rule.duration,
            evaluation_interval=rule.evaluation_interval,
            channels=rule.channels,
            labels=rule.labels,
            annotations=rule.annotations,
            is_enabled=rule.is_enabled,
            created_at=rule.created_at,
            updated_at=rule.updated_at,
        )
        for rule in rules
    ]


@app.get(
    "/api/v1/ops/alert-rules/{rule_id}",
    response_model=AlertRuleResponse,
    tags=["Operations & Monitoring"],
)
async def get_alert_rule_endpoint(rule_id: str, _=Depends(verify_api_key)):
    """获取告警规则详情"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    rule = manager.get_alert_rule(rule_id)

    if not rule:
        raise HTTPException(status_code=404, detail="Alert rule not found")

    return AlertRuleResponse(
        id=rule.id,
        name=rule.name,
        description=rule.description,
        rule_type=rule.rule_type.value,
        severity=rule.severity.value,
        metric=rule.metric,
        condition=rule.condition,
        threshold=rule.threshold,
        duration=rule.duration,
        evaluation_interval=rule.evaluation_interval,
        channels=rule.channels,
        labels=rule.labels,
        annotations=rule.annotations,
        is_enabled=rule.is_enabled,
        created_at=rule.created_at,
        updated_at=rule.updated_at,
    )


@app.patch(
    "/api/v1/ops/alert-rules/{rule_id}",
    response_model=AlertRuleResponse,
    tags=["Operations & Monitoring"],
)
async def update_alert_rule_endpoint(rule_id: str, updates: dict, _=Depends(verify_api_key)):
    """更新告警规则"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    rule = manager.update_alert_rule(rule_id, **updates)

    if not rule:
        raise HTTPException(status_code=404, detail="Alert rule not found")

    return AlertRuleResponse(
        id=rule.id,
        name=rule.name,
        description=rule.description,
        rule_type=rule.rule_type.value,
        severity=rule.severity.value,
        metric=rule.metric,
        condition=rule.condition,
        threshold=rule.threshold,
        duration=rule.duration,
        evaluation_interval=rule.evaluation_interval,
        channels=rule.channels,
        labels=rule.labels,
        annotations=rule.annotations,
        is_enabled=rule.is_enabled,
        created_at=rule.created_at,
        updated_at=rule.updated_at,
    )


@app.delete("/api/v1/ops/alert-rules/{rule_id}", tags=["Operations & Monitoring"])
async def delete_alert_rule_endpoint(rule_id: str, _=Depends(verify_api_key)):
    """删除告警规则"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    success = manager.delete_alert_rule(rule_id)

    if not success:
        raise HTTPException(status_code=404, detail="Alert rule not found")

    return {"success": True, "message": "Alert rule deleted"}


# Alert Channels API
@app.post(
    "/api/v1/ops/alert-channels",
    response_model=AlertChannelResponse,
    tags=["Operations & Monitoring"],
)
async def create_alert_channel_endpoint(
    tenant_id: str, request: AlertChannelCreate, _=Depends(verify_api_key)
):
    """创建告警渠道"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    try:
        channel = manager.create_alert_channel(
            tenant_id=tenant_id,
            name=request.name,
            channel_type=AlertChannelType(request.channel_type),
            config=request.config,
            severity_filter=request.severity_filter,
        )

        return AlertChannelResponse(
            id=channel.id,
            name=channel.name,
            channel_type=channel.channel_type.value,
            config=channel.config,
            severity_filter=channel.severity_filter,
            is_enabled=channel.is_enabled,
            success_count=channel.success_count,
            fail_count=channel.fail_count,
            last_used_at=channel.last_used_at,
            created_at=channel.created_at,
        )
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ops/alert-channels", tags=["Operations & Monitoring"])
async def list_alert_channels_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """列出租户的告警渠道"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    channels = manager.list_alert_channels(tenant_id)

    return [
        AlertChannelResponse(
            id=channel.id,
            name=channel.name,
            channel_type=channel.channel_type.value,
            config=channel.config,
            severity_filter=channel.severity_filter,
            is_enabled=channel.is_enabled,
            success_count=channel.success_count,
            fail_count=channel.fail_count,
            last_used_at=channel.last_used_at,
            created_at=channel.created_at,
        )
        for channel in channels
    ]


@app.post("/api/v1/ops/alert-channels/{channel_id}/test", tags=["Operations & Monitoring"])
async def test_alert_channel_endpoint(channel_id: str, _=Depends(verify_api_key)):
    """测试告警渠道"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    success = manager.test_alert_channel(channel_id)

    if success:
        return {"success": True, "message": "Test alert sent successfully"}
    else:
        raise HTTPException(status_code=400, detail="Failed to send test alert")


# Alerts API
@app.get("/api/v1/ops/alerts", tags=["Operations & Monitoring"])
async def list_alerts_endpoint(
    tenant_id: str,
    status: str | None = None,
    severity: str | None = None,
    limit: int = 100,
    _=Depends(verify_api_key),
):
    """列出租户的告警"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    status_enum = AlertStatus(status) if status else None
    severity_enum = AlertSeverity(severity) if severity else None

    alerts = manager.list_alerts(tenant_id, status=status_enum, severity=severity_enum, limit=limit)

    return [
        AlertResponse(
            id=alert.id,
            rule_id=alert.rule_id,
            severity=alert.severity.value,
            status=alert.status.value,
            title=alert.title,
            description=alert.description,
            metric=alert.metric,
            value=alert.value,
            threshold=alert.threshold,
            labels=alert.labels,
            started_at=alert.started_at,
            resolved_at=alert.resolved_at,
            acknowledged_by=alert.acknowledged_by,
            suppression_count=alert.suppression_count,
        )
        for alert in alerts
    ]


@app.post("/api/v1/ops/alerts/{alert_id}/acknowledge", tags=["Operations & Monitoring"])
async def acknowledge_alert_endpoint(
    alert_id: str, user_id: str = "system", _=Depends(verify_api_key)
):
    """确认告警"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    alert = manager.acknowledge_alert(alert_id, user_id)

    if not alert:
        raise HTTPException(status_code=404, detail="Alert not found")

    return {"success": True, "message": "Alert acknowledged"}


@app.post("/api/v1/ops/alerts/{alert_id}/resolve", tags=["Operations & Monitoring"])
async def resolve_alert_endpoint(alert_id: str, _=Depends(verify_api_key)):
    """解决告警"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    alert = manager.resolve_alert(alert_id)

    if not alert:
        raise HTTPException(status_code=404, detail="Alert not found")

    return {"success": True, "message": "Alert resolved"}


# Resource Metrics API
@app.post("/api/v1/ops/resource-metrics", tags=["Operations & Monitoring"])
async def record_resource_metric_endpoint(
    tenant_id: str,
    resource_type: str,
    resource_id: str,
    metric_name: str,
    metric_value: float,
    unit: str,
    metadata: dict = None,
    _=Depends(verify_api_key),
):
    """记录资源指标"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    try:
        metric = manager.record_resource_metric(
            tenant_id=tenant_id,
            resource_type=ResourceType(resource_type),
            resource_id=resource_id,
            metric_name=metric_name,
            metric_value=metric_value,
            unit=unit,
            metadata=metadata,
        )

        return {
            "id": metric.id,
            "resource_type": metric.resource_type.value,
            "metric_name": metric.metric_name,
            "metric_value": metric.metric_value,
            "unit": metric.unit,
            "timestamp": metric.timestamp,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ops/resource-metrics", tags=["Operations & Monitoring"])
async def get_resource_metrics_endpoint(
    tenant_id: str, metric_name: str, seconds: int = 3600, _=Depends(verify_api_key)
):
    """获取资源指标数据"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    metrics = manager.get_recent_metrics(tenant_id, metric_name, seconds=seconds)

    return [
        {
            "id": m.id,
            "resource_type": m.resource_type.value,
            "resource_id": m.resource_id,
            "metric_name": m.metric_name,
            "metric_value": m.metric_value,
            "unit": m.unit,
            "timestamp": m.timestamp,
        }
        for m in metrics
    ]


# Capacity Planning API
@app.post("/api/v1/ops/capacity-plans", tags=["Operations & Monitoring"])
async def create_capacity_plan_endpoint(
    tenant_id: str,
    resource_type: str,
    current_capacity: float,
    prediction_date: str,
    confidence: float = 0.8,
    _=Depends(verify_api_key),
):
    """创建容量规划"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    try:
        plan = manager.create_capacity_plan(
            tenant_id=tenant_id,
            resource_type=ResourceType(resource_type),
            current_capacity=current_capacity,
            prediction_date=prediction_date,
            confidence=confidence,
        )

        return {
            "id": plan.id,
            "resource_type": plan.resource_type.value,
            "current_capacity": plan.current_capacity,
            "predicted_capacity": plan.predicted_capacity,
            "prediction_date": plan.prediction_date,
            "confidence": plan.confidence,
            "recommended_action": plan.recommended_action,
            "estimated_cost": plan.estimated_cost,
            "created_at": plan.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ops/capacity-plans", tags=["Operations & Monitoring"])
async def list_capacity_plans_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取容量规划列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    plans = manager.get_capacity_plans(tenant_id)

    return [
        {
            "id": plan.id,
            "resource_type": plan.resource_type.value,
            "current_capacity": plan.current_capacity,
            "predicted_capacity": plan.predicted_capacity,
            "prediction_date": plan.prediction_date,
            "confidence": plan.confidence,
            "recommended_action": plan.recommended_action,
            "estimated_cost": plan.estimated_cost,
            "created_at": plan.created_at,
        }
        for plan in plans
    ]


# Auto Scaling API
@app.post("/api/v1/ops/auto-scaling-policies", tags=["Operations & Monitoring"])
async def create_auto_scaling_policy_endpoint(
    tenant_id: str, request: AutoScalingPolicyCreate, _=Depends(verify_api_key)
):
    """创建自动扩缩容策略"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    try:
        policy = manager.create_auto_scaling_policy(
            tenant_id=tenant_id,
            name=request.name,
            resource_type=ResourceType(request.resource_type),
            min_instances=request.min_instances,
            max_instances=request.max_instances,
            target_utilization=request.target_utilization,
            scale_up_threshold=request.scale_up_threshold,
            scale_down_threshold=request.scale_down_threshold,
            scale_up_step=request.scale_up_step,
            scale_down_step=request.scale_down_step,
            cooldown_period=request.cooldown_period,
        )

        return {
            "id": policy.id,
            "name": policy.name,
            "resource_type": policy.resource_type.value,
            "min_instances": policy.min_instances,
            "max_instances": policy.max_instances,
            "target_utilization": policy.target_utilization,
            "scale_up_threshold": policy.scale_up_threshold,
            "scale_down_threshold": policy.scale_down_threshold,
            "is_enabled": policy.is_enabled,
            "created_at": policy.created_at,
        }
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))


@app.get("/api/v1/ops/auto-scaling-policies", tags=["Operations & Monitoring"])
async def list_auto_scaling_policies_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取自动扩缩容策略列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    policies = manager.list_auto_scaling_policies(tenant_id)

    return [
        {
            "id": policy.id,
            "name": policy.name,
            "resource_type": policy.resource_type.value,
            "min_instances": policy.min_instances,
            "max_instances": policy.max_instances,
            "target_utilization": policy.target_utilization,
            "is_enabled": policy.is_enabled,
            "created_at": policy.created_at,
        }
        for policy in policies
    ]


@app.get("/api/v1/ops/scaling-events", tags=["Operations & Monitoring"])
async def list_scaling_events_endpoint(
    tenant_id: str, policy_id: str | None = None, limit: int = 100, _=Depends(verify_api_key)
):
    """获取扩缩容事件列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    events = manager.list_scaling_events(tenant_id, policy_id=policy_id, limit=limit)

    return [
        {
            "id": event.id,
            "policy_id": event.policy_id,
            "action": event.action.value,
            "from_count": event.from_count,
            "to_count": event.to_count,
            "reason": event.reason,
            "status": event.status,
            "started_at": event.started_at,
            "completed_at": event.completed_at,
        }
        for event in events
    ]


# Health Check API
@app.post(
    "/api/v1/ops/health-checks",
    response_model=HealthCheckResponse,
    tags=["Operations & Monitoring"],
)
async def create_health_check_endpoint(
    tenant_id: str, request: HealthCheckCreate, _=Depends(verify_api_key)
):
    """创建健康检查"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    check = manager.create_health_check(
        tenant_id=tenant_id,
        name=request.name,
        target_type=request.target_type,
        target_id=request.target_id,
        check_type=request.check_type,
        check_config=request.check_config,
        interval=request.interval,
        timeout=request.timeout,
        retry_count=request.retry_count,
    )

    return HealthCheckResponse(
        id=check.id,
        name=check.name,
        target_type=check.target_type,
        target_id=check.target_id,
        check_type=check.check_type,
        interval=check.interval,
        timeout=check.timeout,
        is_enabled=check.is_enabled,
        created_at=check.created_at,
    )


@app.get("/api/v1/ops/health-checks", tags=["Operations & Monitoring"])
async def list_health_checks_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取健康检查列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    checks = manager.list_health_checks(tenant_id)

    return [
        {
            "id": check.id,
            "name": check.name,
            "target_type": check.target_type,
            "target_id": check.target_id,
            "check_type": check.check_type,
            "interval": check.interval,
            "timeout": check.timeout,
            "is_enabled": check.is_enabled,
            "created_at": check.created_at,
        }
        for check in checks
    ]


@app.post("/api/v1/ops/health-checks/{check_id}/execute", tags=["Operations & Monitoring"])
async def execute_health_check_endpoint(check_id: str, _=Depends(verify_api_key)):
    """执行健康检查"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    result = await manager.execute_health_check(check_id)

    return {
        "id": result.id,
        "check_id": result.check_id,
        "status": result.status.value,
        "response_time": result.response_time,
        "message": result.message,
        "checked_at": result.checked_at,
    }


# Backup API
@app.post("/api/v1/ops/backup-jobs", tags=["Operations & Monitoring"])
async def create_backup_job_endpoint(
    tenant_id: str, request: BackupJobCreate, _=Depends(verify_api_key)
):
    """创建备份任务"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()

    job = manager.create_backup_job(
        tenant_id=tenant_id,
        name=request.name,
        backup_type=request.backup_type,
        target_type=request.target_type,
        target_id=request.target_id,
        schedule=request.schedule,
        retention_days=request.retention_days,
        encryption_enabled=request.encryption_enabled,
        compression_enabled=request.compression_enabled,
        storage_location=request.storage_location,
    )

    return {
        "id": job.id,
        "name": job.name,
        "backup_type": job.backup_type,
        "target_type": job.target_type,
        "schedule": job.schedule,
        "is_enabled": job.is_enabled,
        "created_at": job.created_at,
    }


@app.get("/api/v1/ops/backup-jobs", tags=["Operations & Monitoring"])
async def list_backup_jobs_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取备份任务列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    jobs = manager.list_backup_jobs(tenant_id)

    return [
        {
            "id": job.id,
            "name": job.name,
            "backup_type": job.backup_type,
            "target_type": job.target_type,
            "schedule": job.schedule,
            "is_enabled": job.is_enabled,
            "created_at": job.created_at,
        }
        for job in jobs
    ]


@app.post("/api/v1/ops/backup-jobs/{job_id}/execute", tags=["Operations & Monitoring"])
async def execute_backup_endpoint(job_id: str, _=Depends(verify_api_key)):
    """执行备份"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    record = manager.execute_backup(job_id)

    if not record:
        raise HTTPException(status_code=404, detail="Backup job not found or disabled")

    return {
        "id": record.id,
        "job_id": record.job_id,
        "status": record.status.value,
        "started_at": record.started_at,
        "storage_path": record.storage_path,
    }


@app.get("/api/v1/ops/backup-records", tags=["Operations & Monitoring"])
async def list_backup_records_endpoint(
    tenant_id: str, job_id: str | None = None, limit: int = 100, _=Depends(verify_api_key)
):
    """获取备份记录列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    records = manager.list_backup_records(tenant_id, job_id=job_id, limit=limit)

    return [
        {
            "id": record.id,
            "job_id": record.job_id,
            "status": record.status.value,
            "size_bytes": record.size_bytes,
            "checksum": record.checksum,
            "started_at": record.started_at,
            "completed_at": record.completed_at,
            "storage_path": record.storage_path,
        }
        for record in records
    ]


# Cost Optimization API
@app.post("/api/v1/ops/cost-reports", tags=["Operations & Monitoring"])
async def generate_cost_report_endpoint(
    tenant_id: str, year: int, month: int, _=Depends(verify_api_key)
):
    """生成成本报告"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    report = manager.generate_cost_report(tenant_id, year, month)

    return {
        "id": report.id,
        "report_period": report.report_period,
        "total_cost": report.total_cost,
        "currency": report.currency,
        "breakdown": report.breakdown,
        "trends": report.trends,
        "anomalies": report.anomalies,
        "created_at": report.created_at,
    }


@app.get("/api/v1/ops/idle-resources", tags=["Operations & Monitoring"])
async def get_idle_resources_endpoint(tenant_id: str, _=Depends(verify_api_key)):
    """获取闲置资源列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    idle_resources = manager.get_idle_resources(tenant_id)

    return [
        {
            "id": resource.id,
            "resource_type": resource.resource_type.value,
            "resource_id": resource.resource_id,
            "resource_name": resource.resource_name,
            "idle_since": resource.idle_since,
            "estimated_monthly_cost": resource.estimated_monthly_cost,
            "currency": resource.currency,
            "reason": resource.reason,
            "recommendation": resource.recommendation,
        }
        for resource in idle_resources
    ]


@app.post("/api/v1/ops/cost-optimization-suggestions", tags=["Operations & Monitoring"])
async def generate_cost_optimization_suggestions_endpoint(
    tenant_id: str, _=Depends(verify_api_key)
):
    """生成成本优化建议"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    suggestions = manager.generate_cost_optimization_suggestions(tenant_id)

    return [
        {
            "id": suggestion.id,
            "category": suggestion.category,
            "title": suggestion.title,
            "description": suggestion.description,
            "potential_savings": suggestion.potential_savings,
            "currency": suggestion.currency,
            "confidence": suggestion.confidence,
            "difficulty": suggestion.difficulty,
            "risk_level": suggestion.risk_level,
            "is_applied": suggestion.is_applied,
            "created_at": suggestion.created_at,
        }
        for suggestion in suggestions
    ]


@app.get("/api/v1/ops/cost-optimization-suggestions", tags=["Operations & Monitoring"])
async def list_cost_optimization_suggestions_endpoint(
    tenant_id: str, is_applied: bool | None = None, _=Depends(verify_api_key)
):
    """获取成本优化建议列表"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    suggestions = manager.get_cost_optimization_suggestions(tenant_id, is_applied=is_applied)

    return [
        {
            "id": suggestion.id,
            "category": suggestion.category,
            "title": suggestion.title,
            "description": suggestion.description,
            "potential_savings": suggestion.potential_savings,
            "confidence": suggestion.confidence,
            "difficulty": suggestion.difficulty,
            "risk_level": suggestion.risk_level,
            "is_applied": suggestion.is_applied,
            "created_at": suggestion.created_at,
        }
        for suggestion in suggestions
    ]


@app.post(
    "/api/v1/ops/cost-optimization-suggestions/{suggestion_id}/apply",
    tags=["Operations & Monitoring"],
)
async def apply_cost_optimization_suggestion_endpoint(
    suggestion_id: str, _=Depends(verify_api_key)
):
    """应用成本优化建议"""
    if not OPS_MANAGER_AVAILABLE:
        raise HTTPException(status_code=503, detail="Operations manager not available")

    manager = get_ops_manager_instance()
    suggestion = manager.apply_cost_optimization_suggestion(suggestion_id)

    if not suggestion:
        raise HTTPException(status_code=404, detail="Suggestion not found")

    return {
        "success": True,
        "message": "Cost optimization suggestion applied",
        "suggestion": {
            "id": suggestion.id,
            "title": suggestion.title,
            "is_applied": suggestion.is_applied,
            "applied_at": suggestion.applied_at,
        },
    }


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)
