#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：deploy.py
模块描述：自动化部署测试模块 API 路由

作者：czj
创建日期：2026-08-12
最后修改：2026-08-12
"""

import logging
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query, status, WebSocket, WebSocketDisconnect
from sqlalchemy.ext.asyncio import AsyncSession

from app.database import get_db
from app.schemas.deploy import (
    DeployServerCreate, DeployServerUpdate, DeployServerResponse, DeployServerListResponse,
    DeployScriptCreate, DeployScriptUpdate, DeployScriptResponse, DeployScriptListResponse,
    DeployExecutionCreate, DeployExecutionResponse, DeployExecutionListResponse,
    DeployLogListResponse, DeployLogResponse,
    TestConnectionResponse,
)
from app.services.deploy_service import DeployService
from app.websocket.manager import manager

logger = logging.getLogger(__name__)

router = APIRouter()


def get_deploy_service(db: AsyncSession = Depends(get_db)) -> DeployService:
    """
    获取部署服务实例（依赖注入）

    Args:
        db (AsyncSession): 数据库会话

    Returns:
        DeployService: 部署服务实例
    """
    return DeployService(db)


# ==================== 服务器管理 ====================


@router.get(
    "/servers",
    response_model=DeployServerListResponse,
    summary="获取服务器列表",
    description="获取所有已配置的目标服务器列表"
)
async def list_servers(
    skip: int = Query(0, ge=0, description="跳过数量"),
    limit: int = Query(50, ge=1, le=500, description="返回数量"),
    keyword: Optional[str] = Query(None, description="搜索关键词"),
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取服务器列表

    Args:
        skip: 跳过数量
        limit: 返回数量
        keyword: 搜索关键词
        service: 部署服务

    Returns:
        DeployServerListResponse: 服务器列表
    """
    servers, total = await service.list_servers(
        skip=skip, limit=limit, keyword=keyword
    )
    return DeployServerListResponse(
        total=total,
        items=[DeployServerResponse.model_validate(s) for s in servers],
    )


@router.get(
    "/servers/{server_id}",
    response_model=DeployServerResponse,
    summary="获取服务器详情",
    description="根据 ID 获取服务器配置详情",
)
async def get_server(
    server_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取服务器详情

    Args:
        server_id: 服务器 ID
        service: 部署服务

    Returns:
        DeployServerResponse: 服务器详情

    Raises:
        HTTPException: 服务器不存在时抛出 404
    """
    server = await service.get_server(server_id)
    if not server:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"服务器不存在: {server_id}",
        )
    return DeployServerResponse.model_validate(server)


@router.post(
    "/servers",
    response_model=DeployServerResponse,
    status_code=status.HTTP_201_CREATED,
    summary="创建服务器",
    description="添加新的目标服务器配置",
)
async def create_server(
    server_data: DeployServerCreate,
    service: DeployService = Depends(get_deploy_service),
):
    """
    创建服务器

    Args:
        server_data: 服务器创建数据
        service: 部署服务

    Returns:
        DeployServerResponse: 创建的服务器
    """
    try:
        server = await service.create_server(server_data)
        return DeployServerResponse.model_validate(server)
    except Exception as e:
        logger.error(f"创建服务器失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建服务器失败: {str(e)}",
        )


@router.put(
    "/servers/{server_id}",
    response_model=DeployServerResponse,
    summary="更新服务器",
    description="更新目标服务器配置信息",
)
async def update_server(
    server_id: str,
    server_data: DeployServerUpdate,
    service: DeployService = Depends(get_deploy_service),
):
    """
    更新服务器

    Args:
        server_id: 服务器 ID
        server_data: 更新数据
        service: 部署服务

    Returns:
        DeployServerResponse: 更新后的服务器

    Raises:
        HTTPException: 服务器不存在时抛出 404
    """
    server = await service.update_server(server_id, server_data)
    if not server:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"服务器不存在: {server_id}",
        )
    return DeployServerResponse.model_validate(server)


@router.delete(
    "/servers/{server_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="删除服务器",
    description="删除指定的目标服务器配置",
)
async def delete_server(
    server_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    删除服务器

    Args:
        server_id: 服务器 ID
        service: 部署服务

    Raises:
        HTTPException: 服务器不存在时抛出 404
    """
    success = await service.delete_server(server_id)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"服务器不存在: {server_id}",
        )


@router.post(
    "/servers/{server_id}/test",
    response_model=TestConnectionResponse,
    summary="测试服务器连接",
    description="测试与目标服务器的 SSH 连接是否正常",
)
async def test_server_connection(
    server_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    测试服务器连接

    Args:
        server_id: 服务器 ID
        service: 部署服务

    Returns:
        TestConnectionResponse: 连接测试结果
    """
    result = await service.test_server_connection(server_id)
    return TestConnectionResponse(**result)


# ==================== 脚本管理 ====================


@router.get(
    "/scripts",
    response_model=DeployScriptListResponse,
    summary="获取脚本列表",
    description="获取所有部署脚本配置列表",
)
async def list_scripts(
    skip: int = Query(0, ge=0, description="跳过数量"),
    limit: int = Query(50, ge=1, le=500, description="返回数量"),
    server_id: Optional[str] = Query(None, description="按服务器筛选"),
    keyword: Optional[str] = Query(None, description="搜索关键词"),
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取脚本列表

    Args:
        skip: 跳过数量
        limit: 返回数量
        server_id: 按服务器筛选
        keyword: 搜索关键词
        service: 部署服务

    Returns:
        DeployScriptListResponse: 脚本列表
    """
    scripts, total = await service.list_scripts(
        skip=skip, limit=limit, server_id=server_id, keyword=keyword
    )
    return DeployScriptListResponse(
        total=total,
        items=[DeployScriptResponse.model_validate(s) for s in scripts],
    )


@router.get(
    "/scripts/{script_id}",
    response_model=DeployScriptResponse,
    summary="获取脚本详情",
    description="根据 ID 获取脚本配置详情",
)
async def get_script(
    script_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取脚本详情

    Args:
        script_id: 脚本配置 ID
        service: 部署服务

    Returns:
        DeployScriptResponse: 脚本详情

    Raises:
        HTTPException: 脚本不存在时抛出 404
    """
    script = await service.get_script(script_id)
    if not script:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"脚本配置不存在: {script_id}",
        )
    return DeployScriptResponse.model_validate(script)


@router.post(
    "/scripts",
    response_model=DeployScriptResponse,
    status_code=status.HTTP_201_CREATED,
    summary="创建脚本配置",
    description="创建新的部署脚本配置",
)
async def create_script(
    script_data: DeployScriptCreate,
    service: DeployService = Depends(get_deploy_service),
):
    """
    创建脚本配置

    Args:
        script_data: 脚本创建数据
        service: 部署服务

    Returns:
        DeployScriptResponse: 创建的脚本配置
    """
    try:
        script = await service.create_script(script_data)
        return DeployScriptResponse.model_validate(script)
    except Exception as e:
        logger.error(f"创建脚本配置失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建脚本配置失败: {str(e)}",
        )


@router.put(
    "/scripts/{script_id}",
    response_model=DeployScriptResponse,
    summary="更新脚本配置",
    description="更新部署脚本配置信息",
)
async def update_script(
    script_id: str,
    script_data: DeployScriptUpdate,
    service: DeployService = Depends(get_deploy_service),
):
    """
    更新脚本配置

    Args:
        script_id: 脚本配置 ID
        script_data: 更新数据
        service: 部署服务

    Returns:
        DeployScriptResponse: 更新后的脚本配置

    Raises:
        HTTPException: 脚本不存在时抛出 404
    """
    script = await service.update_script(script_id, script_data)
    if not script:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"脚本配置不存在: {script_id}",
        )
    return DeployScriptResponse.model_validate(script)


@router.delete(
    "/scripts/{script_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="删除脚本配置",
    description="删除指定的部署脚本配置",
)
async def delete_script(
    script_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    删除脚本配置

    Args:
        script_id: 脚本配置 ID
        service: 部署服务

    Raises:
        HTTPException: 脚本不存在时抛出 404
    """
    success = await service.delete_script(script_id)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"脚本配置不存在: {script_id}",
        )


# ==================== 执行记录管理 ====================


@router.get(
    "/executions",
    response_model=DeployExecutionListResponse,
    summary="获取执行记录列表",
    description="获取部署执行记录列表",
)
async def list_executions(
    skip: int = Query(0, ge=0, description="跳过数量"),
    limit: int = Query(50, ge=1, le=500, description="返回数量"),
    script_id: Optional[str] = Query(None, description="按脚本筛选"),
    status_filter: Optional[str] = Query(None, alias="status", description="按状态筛选"),
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取执行记录列表

    Args:
        skip: 跳过数量
        limit: 返回数量
        script_id: 按脚本筛选
        status_filter: 按状态筛选
        service: 部署服务

    Returns:
        DeployExecutionListResponse: 执行记录列表
    """
    executions, total = await service.list_executions(
        skip=skip, limit=limit, script_id=script_id, status=status_filter
    )
    return DeployExecutionListResponse(
        total=total,
        items=[DeployExecutionResponse.model_validate(e) for e in executions],
    )


@router.get(
    "/executions/{execution_id}",
    response_model=DeployExecutionResponse,
    summary="获取执行记录详情",
    description="根据 ID 获取部署执行记录详情",
)
async def get_execution(
    execution_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取执行记录详情

    Args:
        execution_id: 执行记录 ID
        service: 部署服务

    Returns:
        DeployExecutionResponse: 执行记录详情

    Raises:
        HTTPException: 执行记录不存在时抛出 404
    """
    execution = await service.get_execution(execution_id)
    if not execution:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"执行记录不存在: {execution_id}",
        )
    return DeployExecutionResponse.model_validate(execution)


@router.post(
    "/executions",
    response_model=DeployExecutionResponse,
    status_code=status.HTTP_201_CREATED,
    summary="创建执行记录",
    description="创建部署执行记录（尚未开始执行）",
)
async def create_execution(
    exec_data: DeployExecutionCreate,
    service: DeployService = Depends(get_deploy_service),
):
    """
    创建执行记录

    Args:
        exec_data: 创建执行记录数据
        service: 部署服务

    Returns:
        DeployExecutionResponse: 创建的执行记录
    """
    try:
        execution = await service.create_execution(exec_data)
        return DeployExecutionResponse.model_validate(execution)
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e),
        )
    except Exception as e:
        logger.error(f"创建执行记录失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"创建执行记录失败: {str(e)}",
        )


@router.post(
    "/executions/{execution_id}/run",
    response_model=DeployExecutionResponse,
    summary="开始执行部署",
    description="开始执行指定的部署任务，通过 WebSocket 实时推送日志",
)
async def run_execution(
    execution_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    开始执行部署

    Args:
        execution_id: 执行记录 ID
        service: 部署服务

    Returns:
        DeployExecutionResponse: 更新后的执行记录
    """
    try:
        execution = await service.run_execution(execution_id)
        return DeployExecutionResponse.model_validate(execution)
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=str(e),
        )
    except Exception as e:
        logger.error(f"执行部署失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"执行部署失败: {str(e)}",
        )


@router.post(
    "/executions/{execution_id}/cancel",
    response_model=DeployExecutionResponse,
    summary="取消执行",
    description="取消正在执行的部署任务",
)
async def cancel_execution(
    execution_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    取消执行

    Args:
        execution_id: 执行记录 ID
        service: 部署服务

    Returns:
        DeployExecutionResponse: 更新后的执行记录
    """
    execution = await service.get_execution(execution_id)
    if not execution:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"执行记录不存在: {execution_id}",
        )

    if execution.status != "running":
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"执行记录状态为 {execution.status}，无法取消",
        )

    success = await service.cancel_execution(execution_id)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="取消执行失败",
        )

    execution = await service.get_execution(execution_id)
    return DeployExecutionResponse.model_validate(execution)


@router.delete(
    "/executions/{execution_id}",
    status_code=status.HTTP_204_NO_CONTENT,
    summary="删除执行记录",
    description="删除指定的部署执行记录",
)
async def delete_execution(
    execution_id: str,
    service: DeployService = Depends(get_deploy_service),
):
    """
    删除执行记录

    Args:
        execution_id: 执行记录 ID
        service: 部署服务

    Raises:
        HTTPException: 执行记录不存在时抛出 404
    """
    success = await service.delete_execution(execution_id)
    if not success:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"执行记录不存在: {execution_id}",
        )


@router.get(
    "/executions/{execution_id}/logs",
    response_model=DeployLogListResponse,
    summary="获取执行日志",
    description="获取指定执行记录的完整日志",
)
async def get_execution_logs(
    execution_id: str,
    skip: int = Query(0, ge=0, description="跳过数量"),
    limit: int = Query(5000, ge=1, le=50000, description="返回数量"),
    service: DeployService = Depends(get_deploy_service),
):
    """
    获取执行日志

    Args:
        execution_id: 执行记录 ID
        skip: 跳过数量
        limit: 返回数量
        service: 部署服务

    Returns:
        DeployLogListResponse: 日志列表
    """
    logs, total = await service.get_execution_logs(
        execution_id=execution_id, skip=skip, limit=limit
    )
    return DeployLogListResponse(
        total=total,
        items=[DeployLogResponse.model_validate(l) for l in logs],
    )


# ==================== WebSocket 实时日志 ====================


@router.websocket(
    "/ws/executions/{execution_id}",
)
async def websocket_execution_log(
    websocket: WebSocket,
    execution_id: str,
):
    """
    WebSocket 实时部署日志推送

    连接后自动订阅指定执行记录的实时日志推送。
    消息类型：deploy_start / deploy_output / deploy_stderr / deploy_answer_sent / deploy_complete / deploy_error

    Args:
        websocket: WebSocket 连接
        execution_id: 执行记录 ID
    """
    await manager.connect(websocket, execution_id)
    try:
        while True:
            # 保持连接，接收客户端消息（可用于心跳）
            await websocket.receive_text()
    except WebSocketDisconnect:
        await manager.disconnect(websocket, execution_id)
    except Exception as e:
        logger.error(f"WebSocket 异常: {e}")
        await manager.disconnect(websocket, execution_id)