#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：main.py
模块描述：FastAPI 应用入口，负责启动 Web 服务和注册路由

作者：czj
创建日期：2026-07-09
最后修改：2026-07-11
"""

import asyncio
import logging
import os
import sys
from contextlib import asynccontextmanager
from pathlib import Path

# Windows 平台设置 ProactorEventLoop 以支持子进程操作
if sys.platform == 'win32':
    asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

from app.config import settings
from app.database import init_db
from app.routers import modules, cases, executions, recorder, stats, reports, cleanup, batch, dependencies, security, ai_locator, device_sim, system, element_locator, login_template, smart_locate

# 配置日志
logging.basicConfig(
    level=logging.INFO if not settings.DEBUG else logging.DEBUG,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    """
    应用生命周期管理

    在应用启动时初始化数据库，在关闭时清理资源。

    Yields:
        None
    """
    # 启动时
    logger.info(f"正在启动 {settings.APP_NAME} v{settings.APP_VERSION}")

    # 确保必要的目录存在
    Path(settings.SCREENSHOT_DIR).mkdir(parents=True, exist_ok=True)
    Path(settings.REPORT_DIR).mkdir(parents=True, exist_ok=True)
    logger.info("目录初始化完成")

    await init_db()
    logger.info("数据库初始化完成")

    # 初始化默认登录模板
    try:
        from app.services.login_template_service import LoginTemplateService
        from app.database import async_session_maker
        async with async_session_maker() as session:
            service = LoginTemplateService(session)
            await service.ensure_default_templates()
            await session.commit()
        logger.info("默认登录模板初始化完成")
    except Exception as e:
        logger.warning(f"默认登录模板初始化失败（非致命）: {e}")

    yield

    # 关闭时
    logger.info("应用正在关闭...")


# 创建 FastAPI 应用实例
app = FastAPI(
    title=settings.APP_NAME,
    description="Web 可视化自动化测试平台 API - 支持用例录制、执行、结果分析",
    version=settings.APP_VERSION,
    lifespan=lifespan,
    docs_url="/docs",
    redoc_url="/redoc",
)

# 配置 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# ==================== 注册路由 ====================

app.include_router(
    modules.router,
    prefix="/api/modules",
    tags=["模块管理"]
)

app.include_router(
    cases.router,
    prefix="/api/cases",
    tags=["用例管理"]
)

app.include_router(
    executions.router,
    prefix="/api/executions",
    tags=["执行管理"]
)

app.include_router(
    recorder.router,
    prefix="/api/recorder",
    tags=["用例录制"]
)

app.include_router(
    stats.router,
    prefix="/api/stats",
    tags=["统计分析"]
)

app.include_router(
    reports.router,
    prefix="/api/reports",
    tags=["报告管理"]
)

app.include_router(
    cleanup.router,
    prefix="/api/cleanup",
    tags=["数据清理"]
)

app.include_router(
    batch.router,
    prefix="/api/batch",
    tags=["批量操作"]
)

app.include_router(
    dependencies.router,
    prefix="/api",
    tags=["依赖管理"]
)

app.include_router(
    security.router,
    prefix="/api/security",
    tags=["安全测试"]
)

app.include_router(
    ai_locator.router,
    prefix="/api/ai",
    tags=["AI定位"]
)

app.include_router(
    device_sim.router,
    prefix="/api/device-sim",
    tags=["设备模拟"]
)

app.include_router(
    system.router,
    prefix="/api/system",
    tags=["系统管理"]
)

app.include_router(
    element_locator.router,
    prefix="/api/element",
    tags=["元素定位"]
)

app.include_router(
    smart_locate.router,
    prefix="/api/element",
    tags=["智能定位"]
)

app.include_router(
    login_template.router,
    prefix="/api/login-templates",
    tags=["登录模板"]
)


# ==================== 根路径 ====================

@app.get("/", tags=["健康检查"])
async def root():
    """
    根路径 — 容器部署时返回前端页面，否则返回健康检查 JSON

    Returns:
        FileResponse (前端 dist 存在时) 或 dict (健康检查)
    """
    # 容器部署时，前端 dist 存在，直接返回 index.html
    FRONTEND_DIST = Path(__file__).parent.parent / "frontend" / "dist"
    if FRONTEND_DIST.exists():
        return FileResponse(str(FRONTEND_DIST / "index.html"))

    # 本地开发时，返回健康检查 JSON
    return {
        "status": "ok",
        "message": f"{settings.APP_NAME} API 服务运行中",
        "version": settings.APP_VERSION
    }


@app.get("/health", tags=["健康检查"])
async def health_check():
    """
    详细健康检查

    返回服务和数据库连接状态。

    Returns:
        dict: 详细健康状态
    """
    return {
        "status": "healthy",
        "service": settings.APP_NAME,
        "version": settings.APP_VERSION,
        "debug": settings.DEBUG
    }


# ==================== 文件服务 ====================

@app.get("/api/files/{file_path:path}", tags=["文件服务"])
async def serve_file(file_path: str):
    """
    提供截图等静态文件访问

    支持路径格式：自定义相对路径

    Args:
        file_path (str): 文件相对路径

    Returns:
        FileResponse: 文件响应
    """
    # 支持多种可能的路径
    possible_paths = [
        Path(file_path),                      # 直接路径
        Path("data") / file_path,             # data/ 前缀
        Path(settings.SCREENSHOT_DIR) / Path(file_path).name,  # 截图目录
    ]

    for p in possible_paths:
        resolved = p.resolve()
        if resolved.exists() and resolved.is_file():
            return FileResponse(str(resolved))

    raise HTTPException(
        status_code=404,
        detail=f"文件不存在: {file_path}"
    )


# ==================== 前端静态文件服务（容器化部署用） ====================

FRONTEND_DIST = Path(__file__).parent.parent / "frontend" / "dist"

if FRONTEND_DIST.exists():
    logger.info(f"挂载前端静态文件: {FRONTEND_DIST}")

    # 挂载静态资源目录（JS/CSS/图片等）
    app.mount("/assets", StaticFiles(directory=FRONTEND_DIST / "assets"), name="assets")

    # 前端 SPA 路由：所有非 API 路径回退到 index.html
    @app.get("/{full_path:path}")
    async def serve_spa(full_path: str):
        """
        前端 SPA 入口，所有非 API 路径返回 index.html

        注意：此路由必须注册在所有 API 路由之后，避免覆盖 API 路由。
        """
        file_path = FRONTEND_DIST / full_path
        if file_path.exists() and file_path.is_file():
            return FileResponse(str(file_path))
        return FileResponse(str(FRONTEND_DIST / "index.html"))