"""FastAPI 主入口。"""

import asyncio
import logging
import os
from contextlib import asynccontextmanager
from typing import Any

import inngest
import inngest.fast_api
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel

from butler.config import settings
from butler.gateway.base import BaseGateway
from butler.gateway.napcat import NapCatGateway
from butler.gateway.telegram import TelegramGateway
from butler.workflows import inngest_client
from butler.workflows.handle_message import handle_im_message
from butler.workflows.guardrail import command_guardrail

logging.basicConfig(
    level=getattr(logging, settings.log_level),
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
)
logger = logging.getLogger(__name__)


class GatewayManager:
    """多通道网关管理器。"""

    def __init__(self) -> None:
        self.gateways: dict[str, BaseGateway] = {}
        self._tasks: list[asyncio.Task[None]] = []

    def register(self, gateway: BaseGateway) -> None:
        """注册网关。"""
        self.gateways[gateway.name] = gateway

    async def start_all(self) -> None:
        """启动所有网关。"""
        for name, gw in self.gateways.items():
            gw.on_message(self._handle_message)
            task = asyncio.create_task(gw.start())
            self._tasks.append(task)
            logger.info(f"Gateway '{name}' started")

    async def _handle_message(self, user_id: str, content: str, channel: str) -> None:
        """统一消息回调：处理并回复。"""
        logger.info(f"[{channel}] Message from {user_id}: {content[:50]}...")
        try:
            result = await _process_message(user_id, content)
            reply = result.get("reply", "")
            if reply and channel in self.gateways:
                await self.gateways[channel].send_message(user_id, reply)
                logger.info(f"[{channel}] Replied to {user_id}")
        except Exception as e:
            logger.error(f"[{channel}] Error handling message: {e}")
            if channel in self.gateways:
                await self.gateways[channel].send_message(user_id, f"处理出错：{e}")

    async def stop_all(self) -> None:
        """停止所有网关。"""
        for name, gw in self.gateways.items():
            await gw.stop()
            logger.info(f"Gateway '{name}' stopped")
        for task in self._tasks:
            task.cancel()


def _build_gateways() -> GatewayManager:
    """根据配置构建网关实例。"""
    mgr = GatewayManager()

    for channel in settings.enabled_channels:
        if channel == "telegram" and settings.telegram_bot_token:
            mgr.register(TelegramGateway(
                bot_token=settings.telegram_bot_token,
                allowed_chats=settings.telegram_allowed_chats,
            ))
        elif channel == "napcat":
            mgr.register(NapCatGateway(
                ws_url=settings.napcat_ws_url,
                access_token=settings.napcat_token,
            ))
        else:
            logger.warning(f"Unknown channel: {channel}")

    return mgr


gateway_manager = _build_gateways()


async def _process_message(user_id: str, content: str) -> dict[str, Any]:
    """核心消息处理逻辑：自然语言 → 命令 → 执行 → 总结。"""
    from butler.session.wrapper import TmuxWrapper
    from butler.session import SessionMode
    from butler.security.guardrail import is_dangerous_command, get_danger_reason
    from butler.agent import agent
    from butler.skills.log_skill import LogSkill

    tmux = TmuxWrapper(session_prefix=settings.session_prefix)
    log_skill = LogSkill()

    # 检查权限
    if not settings.is_user_allowed(user_id):
        return {"reply": f"用户 {user_id} 无权限使用此服务", "mode": "unauthorized"}

    # 获取或创建会话
    session_name = tmux.get_or_create_session(user_id)

    # 检测模式
    current_mode = tmux.detect_mode(session_name)

    # 交互模式直接透传
    if current_mode == SessionMode.INTERACTIVE:
        tmux.send_keys(session_name, content, enter=False)
        raw_output = tmux.capture_pane(session_name)
        return {"reply": "已发送按键到交互程序", "mode": "interactive"}

    # 自然语言模式：Claude 理解意图 → 生成命令
    command = content

    if agent.is_available():
        context = {
            "pwd": tmux.capture_pane(session_name)[-500:] if tmux.session_exists(session_name) else "",
        }
        command = await agent.understand(content, context)
        logger.info(f"Agent: '{content}' -> '{command}'")

    # 危险命令检查
    if settings.guardrail_enabled and is_dangerous_command(command):
        reason = get_danger_reason(command) or "潜在危险命令"
        return {
            "reply": f"⚠️ 该操作需要确认：{reason}。将执行 `{command}`，确认吗？",
            "mode": "pending_approval",
            "command": command,
        }

    # 执行命令
    tmux.send_keys(session_name, command, enter=True)
    await asyncio.sleep(0.5)
    raw_output = tmux.capture_pane(session_name)

    # 记录到日志
    log_skill.log_command(user_id, session_name, command, raw_output[:2000])

    # Claude 总结输出
    if agent.is_available():
        reply = await agent.summarize(content, command, raw_output)
    else:
        reply = f"执行了 `{command}`\n{raw_output[-500:]}"

    return {"reply": reply, "mode": "nl", "command": command}


@asynccontextmanager
async def lifespan(app: FastAPI):
    """应用生命周期管理。"""
    logger.info("Starting Butler-Shell...")

    await gateway_manager.start_all()

    yield

    logger.info("Shutting down Butler-Shell...")
    await gateway_manager.stop_all()


app = FastAPI(
    title="Butler-Shell",
    description="Persistent Shell Assistant with Multi-Channel Bridge",
    version="0.2.0",
    lifespan=lifespan,
)

# 注册 Inngest 函数
_signing_key = settings.inngest_signing_key or os.environ.get(
    "INNGEST_SIGNING_KEY",
    "signkey-dev-placeholder-1234567890123456789012345678901234567890"
)
_dev_client = inngest.Inngest(
    app_id="butler-shell",
    event_key=settings.inngest_event_key or "dev_key",
    signing_key=_signing_key,
    is_production=bool(settings.inngest_signing_key),
)

inngest.fast_api.serve(
    app,
    _dev_client,
    [handle_im_message, command_guardrail],
    serve_path="/api/inngest",
)


@app.get("/health")
async def health() -> dict[str, str]:
    """服务存活检查。"""
    return {"status": "ok"}


@app.get("/ready")
async def ready() -> dict[str, Any]:
    """服务就绪检查。"""
    import subprocess
    try:
        subprocess.run(["tmux", "-V"], capture_output=True, check=True)
        tmux_ok = True
    except Exception:
        tmux_ok = False

    channels = {name: "connected" for name in gateway_manager.gateways}

    return {
        "status": "ready" if tmux_ok else "degraded",
        "tmux": tmux_ok,
        "channels": channels,
    }


class MockMessage(BaseModel):
    """Mock 消息请求。"""
    user_id: str = "test_user_001"
    content: str


@app.post("/api/mock/message")
async def mock_message(msg: MockMessage) -> dict[str, Any]:
    """自然语言交互端点：输入自然语言，返回自然语言总结。"""
    return await _process_message(msg.user_id, msg.content)


@app.get("/api/raw-output")
async def get_raw_output(user_id: str, limit: int = 5) -> dict[str, Any]:
    """获取命令的原始输出（从日志）。"""
    from butler.skills.log_skill import LogSkill

    log_skill = LogSkill()
    conn = __import__("sqlite3").connect(log_skill.db_path)
    cursor = conn.execute(
        "SELECT command, output, timestamp FROM shell_logs WHERE user_id = ? ORDER BY timestamp DESC LIMIT ?",
        (user_id, limit),
    )
    rows = cursor.fetchall()
    conn.close()

    logs = [
        {"command": row[0], "output": row[1], "timestamp": row[2]}
        for row in rows
    ]
    return {"user_id": user_id, "logs": logs}


@app.post("/api/approval")
async def handle_approval(user_id: str, approved: bool) -> dict[str, str]:
    """处理用户审批响应。"""
    await inngest_client.send(
        inngest.Event(
            name="im/approval-response",
            data={"user_id": user_id, "approved": approved},
        )
    )
    return {"status": "sent"}


def main() -> None:
    """启动服务。"""
    uvicorn.run("butler.main:app", host="0.0.0.0", port=9600, reload=False)


if __name__ == "__main__":
    main()
