#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：module_service.py
模块描述：测试模块业务服务

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

import logging
from typing import Optional, List, Tuple

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, update, delete
from sqlalchemy.orm import selectinload

from app.models.module import Module
from app.models.test_case import TestCase
from app.schemas.module import ModuleCreate, ModuleUpdate
from app.utils.id_generator import generate_id

logger = logging.getLogger(__name__)


class ModuleService:
    """
    模块服务类

    负责测试模块的业务逻辑处理，包括模块的增删改查和统计计算。

    Attributes:
        db (AsyncSession): 数据库会话对象

    Example:
        >>> from app.database import get_db
        >>> service = ModuleService(db)
        >>> module = await service.create(ModuleCreate(name="登录模块"))
        >>> print(module.name)
        '登录模块'
    """

    def __init__(self, db: AsyncSession):
        """
        初始化模块服务

        Args:
            db (AsyncSession): 数据库会话对象，用于数据持久化操作

        Raises:
            ValueError: 当 db 为 None 时抛出
        """
        if db is None:
            raise ValueError("数据库会话不能为空")
        self.db = db

    async def list(
        self,
        skip: int = 0,
        limit: int = 100,
        order_by: str = "order",
        module_type: Optional[str] = None,
        case_type: Optional[str] = None,
    ) -> Tuple[List[Module], int]:
        """
        获取模块列表（分页）

        按排序号升序返回模块列表，同时统计每个模块下的用例数量。

        Args:
            skip (int): 跳过的记录数，默认0
            limit (int): 返回记录数上限，默认100
            order_by (str): 排序字段，默认 "order"
            module_type (Optional[str]): 模块类型筛选 (standard/custom)
            case_type (Optional[str]): 用例类型筛选 (ui/security/all)
                - ui: 只返回 UI 测试模块（ID不以 sec_ 开头）
                - security: 只返回安全测试模块（ID以 sec_ 开头）
                - all 或 None: 返回全部模块

        Returns:
            Tuple[List[Module], int]: (模块列表, 总数)

        Example:
            >>> modules, total = await service.list(skip=0, limit=20, case_type="ui")
            >>> print(f"共 {total} 个UI测试模块")
        """
        # 构建查询条件
        conditions = []
        if module_type:
            conditions.append(Module.module_type == module_type)

        # 按用例类型过滤模块（通过模块ID前缀区分）
        if case_type == "ui":
            # UI测试模块：ID不以 sec_ 开头
            conditions.append(~Module.id.startswith("sec_"))
        elif case_type == "security":
            # 安全测试模块：ID以 sec_ 开头
            conditions.append(Module.id.startswith("sec_"))

        # 查询总数
        count_query = select(func.count(Module.id))
        if conditions:
            count_query = count_query.where(*conditions)
        total_result = await self.db.execute(count_query)
        total = total_result.scalar() or 0

        # 查询模块列表
        query = (
            select(Module)
            .order_by(Module.order.asc(), Module.created_at.desc())
            .offset(skip)
            .limit(limit)
        )
        if conditions:
            query = query.where(*conditions)
        result = await self.db.execute(query)
        modules = list(result.scalars().all())

        # 统计每个模块的用例数量
        for module in modules:
            case_count_query = select(func.count(TestCase.id)).where(
                TestCase.module_id == module.id
            )
            case_result = await self.db.execute(case_count_query)
            module.case_count = case_result.scalar() or 0

        return modules, total

    async def get(self, module_id: str) -> Optional[Module]:
        """
        获取单个模块详情

        Args:
            module_id (str): 模块ID

        Returns:
            Optional[Module]: 模块对象，不存在时返回 None

        Example:
            >>> module = await service.get("module_abc123")
            >>> if module:
            ...     print(module.name)
        """
        query = select(Module).where(Module.id == module_id)
        result = await self.db.execute(query)
        module = result.scalar_one_or_none()

        if module:
            # 统计用例数量
            case_count_query = select(func.count(TestCase.id)).where(
                TestCase.module_id == module.id
            )
            case_result = await self.db.execute(case_count_query)
            module.case_count = case_result.scalar() or 0

        return module

    async def create(self, data: ModuleCreate) -> Module:
        """
        创建测试模块

        根据提供的参数创建新的测试模块，并保存到数据库。

        Args:
            data (ModuleCreate): 模块创建数据

        Returns:
            Module: 创建成功的模块对象

        Raises:
            ValueError: 当 name 为空或超过长度限制时抛出

        Example:
            >>> module = await service.create(ModuleCreate(
            ...     name="登录模块",
            ...     description="用户登录相关功能测试"
            ... ))
        """
        # 参数校验
        if not data.name or not data.name.strip():
            raise ValueError("模块名称不能为空")
        if len(data.name) > 100:
            raise ValueError("模块名称不能超过100字符")

        # 获取最大排序号
        max_order_query = select(func.max(Module.order))
        result = await self.db.execute(max_order_query)
        max_order = result.scalar() or 0

        # 创建模块对象
        module = Module(
            id=generate_id("module"),
            name=data.name.strip(),
            description=data.description or "",
            icon=data.icon or "folder",
            order=max_order + 1,
            module_type=data.module_type or "standard",
            parent_id=data.parent_id if hasattr(data, 'parent_id') else None,
        )

        self.db.add(module)
        await self.db.flush()
        await self.db.refresh(module)

        logger.info(f"模块创建成功: id={module.id}, name={module.name}")
        return module

    async def update(self, module_id: str, data: ModuleUpdate) -> Optional[Module]:
        """
        更新模块信息

        只更新传入的非空字段。

        Args:
            module_id (str): 模块ID
            data (ModuleUpdate): 更新数据

        Returns:
            Optional[Module]: 更新后的模块对象，不存在时返回 None

        Example:
            >>> updated = await service.update("module_001", ModuleUpdate(name="新名称"))
        """
        # 检查模块是否存在
        module = await self.get(module_id)
        if not module:
            logger.warning(f"模块不存在: {module_id}")
            return None

        # 只更新传入的非空字段（parent_id 特殊处理：支持设为空字符串来清除）
        update_data = data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            if field == "parent_id" and value == "":
                # 空字符串表示清除父模块
                setattr(module, field, None)
            elif value is not None:
                setattr(module, field, value)

        await self.db.flush()
        await self.db.refresh(module)

        logger.info(f"模块更新成功: id={module.id}")
        return module

    async def delete(self, module_id: str) -> bool:
        """
        删除模块

        删除模块及其下所有用例（级联删除）。

        Args:
            module_id (str): 模块ID

        Returns:
            bool: 删除是否成功

        Example:
            >>> deleted = await service.delete("module_001")
            >>> print("删除成功" if deleted else "模块不存在")
        """
        module = await self.get(module_id)
        if not module:
            logger.warning(f"模块不存在，无法删除: {module_id}")
            return False

        await self.db.delete(module)
        await self.db.flush()

        logger.info(f"模块删除成功: id={module_id}, name={module.name}")
        return True

    async def get_stats(self, module_id: str) -> Optional[dict]:
        """
        获取模块统计信息

        Args:
            module_id (str): 模块ID

        Returns:
            Optional[dict]: 统计信息，包含总用例数和通过数
        """
        module = await self.get(module_id)
        if not module:
            return None

        # 统计用例数量
        total_query = select(func.count(TestCase.id)).where(
            TestCase.module_id == module_id
        )
        total_result = await self.db.execute(total_query)
        total_cases = total_result.scalar() or 0

        # 统计激活用例数量
        active_query = select(func.count(TestCase.id)).where(
            TestCase.module_id == module_id,
            TestCase.status == "active"
        )
        active_result = await self.db.execute(active_query)
        active_cases = active_result.scalar() or 0

        return {
            "module_id": module_id,
            "module_name": module.name,
            "total_cases": total_cases,
            "active_cases": active_cases
        }
