#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：api_test_service.py
模块描述：接口测试服务层，提供用例执行、结果查询等业务逻辑

作者：czj
创建日期：2026-08-17
"""

import asyncio
import json
import logging
from datetime import datetime
from typing import Optional, List, Dict, Any, Tuple

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, desc

from app.models.execution import Execution
from app.models.case_result import CaseResult
from app.models.test_case import TestCase
from app.models.module import Module
from app.models.security_config import SecurityConfig
from app.executors.api_test_executor import ApiTestExecutor, ApiCaseResult
from app.utils.id_generator import generate_id
from app.config import settings

logger = logging.getLogger(__name__)


class ApiTestService:
    """
    接口测试服务类

    提供接口测试用例执行、结果查询等功能。
    """

    def __init__(self, db: AsyncSession):
        """
        初始化接口测试服务

        Args:
            db: 数据库会话
        """
        self.db = db

    # ==================== 用例管理 ====================

    async def get_api_cases(
        self,
        skip: int = 0,
        limit: int = 20,
        module_id: Optional[str] = None,
        keyword: Optional[str] = None,
        method: Optional[str] = None,
    ) -> Tuple[List[TestCase], int]:
        """
        获取接口测试用例列表

        Args:
            skip: 跳过记录数
            limit: 返回记录数
            module_id: 模块ID筛选
            keyword: 关键字搜索
            method: 请求方法筛选

        Returns:
            tuple: (用例列表, 总数)
        """
        conditions = [TestCase.case_type == "api"]

        if module_id:
            conditions.append(TestCase.module_id == module_id)
        if keyword:
            conditions.append(TestCase.name.ilike(f"%{keyword}%"))
        if method:
            # 从 steps JSON 中过滤方法，在查询后处理或直接返回全部
            pass

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

        # 查询列表
        result = await self.db.execute(
            select(TestCase)
            .where(*conditions)
            .order_by(desc(TestCase.updated_at))
            .offset(skip)
            .limit(limit)
        )
        cases = list(result.scalars().all())

        return cases, total

    async def get_api_case(self, case_id: str) -> Optional[TestCase]:
        """获取单个接口测试用例"""
        result = await self.db.execute(
            select(TestCase).where(TestCase.id == case_id, TestCase.case_type == "api")
        )
        return result.scalar_one_or_none()

    # ==================== 执行管理 ====================

    async def create_execution(
        self,
        case_ids: List[str],
        target_url: Optional[str] = None,
        config_id: Optional[str] = None,
        name: str = "",
    ) -> Execution:
        """
        创建接口测试执行记录

        Args:
            case_ids: 要执行的用例ID列表
            target_url: 目标服务器 base URL
            config_id: 安全测试配置ID（复用登录配置）
            name: 执行名称

        Returns:
            Execution: 执行记录
        """
        execution_id = generate_id("exec")
        total_cases = len(case_ids)

        # 构建执行配置
        exec_config = {
            "case_ids": case_ids,
            "target_url": target_url,
            "config_id": config_id,
        }

        execution = Execution(
            id=execution_id,
            name=name or f"接口测试-{datetime.now().strftime('%Y%m%d_%H%M%S')}",
            trigger_type="manual",
            trigger_by="api_test",
            total_cases=total_cases,
            status="pending",
            case_type="api",
            config=exec_config,
        )

        self.db.add(execution)
        await self.db.commit()
        await self.db.refresh(execution)

        logger.info(f"创建接口测试执行: {execution_id}, 用例数: {total_cases}")
        return execution

    async def run_execution(self, execution_id: str):
        """
        执行接口测试

        Args:
            execution_id: 执行ID
        """
        execution = await self._get_execution(execution_id)
        if not execution:
            logger.error(f"执行记录不存在: {execution_id}")
            return

        case_ids = execution.config.get("case_ids", [])
        target_url = execution.config.get("target_url", "")
        config_id = execution.config.get("config_id")

        # 获取用例
        cases = await self._get_cases_by_ids(case_ids)
        if not cases:
            logger.warning("没有找到要执行的用例")
            execution.status = "completed"
            await self.db.commit()
            return

        # 构建客户端配置
        client_config = await self._build_client_config(target_url, config_id)

        # 更新执行状态
        execution.status = "running"
        execution.start_time = datetime.utcnow()
        await self.db.commit()

        try:
            # 在线程池中执行（避免阻塞 asyncio）
            loop = asyncio.get_event_loop()

            # 把 ORM 对象转成纯字典
            def _to_plain_steps(steps_val):
                if steps_val is None:
                    return {}
                if isinstance(steps_val, str):
                    try:
                        return json.loads(steps_val)
                    except Exception:
                        return {}
                if isinstance(steps_val, dict):
                    return steps_val
                return {}

            plain_cases = [
                {
                    "id": c.id,
                    "name": c.name,
                    "steps": _to_plain_steps(c.steps),
                }
                for c in cases
            ]

            # 同步执行函数
            def sync_run():
                try:
                    executor = ApiTestExecutor(client_config)
                    executor.start()
                    return executor.execute_cases(plain_cases)
                except Exception as e:
                    import traceback
                    logger.error(f"sync_run 异常: {e}")
                    logger.error(traceback.format_exc())
                    raise

            # 在线程池中执行
            results: List[ApiCaseResult] = await loop.run_in_executor(
                None, sync_run
            )

            # 保存结果
            passed = 0
            failed = 0
            for r in results:
                await self._save_case_result(execution_id, r)
                if r.status == "passed":
                    passed += 1
                else:
                    failed += 1

            # 更新执行统计
            execution.passed = passed
            execution.failed = failed
            execution.status = "completed"
            execution.end_time = datetime.utcnow()
            execution.duration = (execution.end_time - execution.start_time).total_seconds()
            execution.pass_rate = round((passed / execution.total_cases) * 100, 2) if execution.total_cases > 0 else 0

            await self.db.commit()
            logger.info(f"接口测试执行完成: {execution_id}, 通过: {passed}, 失败: {failed}")

        except Exception as e:
            logger.error(f"接口测试执行异常: {e}")
            import traceback
            logger.error(traceback.format_exc())
            execution.status = "failed"
            execution.end_time = datetime.utcnow()
            await self.db.commit()

    async def _build_client_config(
        self,
        target_url: Optional[str] = None,
        config_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        构建 HttpClient 客户端配置

        Args:
            target_url: 目标服务器 URL
            config_id: 安全测试配置 ID

        Returns:
            dict: 客户端配置
        """
        # 默认配置（与安全测试共用账号体系）
        config = {
            "target": {
                "base_url": target_url or settings.TARGET_URL,
                "verify_ssl": False,
                "timeout": 30,
            },
            "accounts": {
                "superadmin": {
                    "username": "admin@xty",
                    "password": "Ubains@13579",
                },
                "admin": {
                    "username": "admin@xty",
                    "password": "Ubains@13579",
                },
                "user": {
                    "username": "admin@xty",
                    "password": "Ubains@13579",
                },
                "captcha": "csba",
            },
            "auth": {
                "login_path": "/platform/api/auth/login",
                "captcha_path": "/platform/api/code",
            },
        }

        # 有安全测试配置时，复用其 target/accounts/auth
        if config_id:
            try:
                result = await self.db.execute(
                    select(SecurityConfig).where(SecurityConfig.id == config_id)
                )
                config_obj = result.scalar_one_or_none()
                if config_obj:
                    merged = config_obj.to_client_config()
                    # 合并 target/accounts/auth（仅合并非空项）
                    for key in ("target", "accounts", "auth"):
                        if merged.get(key):
                            config[key] = merged[key]
            except Exception as e:
                logger.warning(f"复用安全测试配置 {config_id} 失败: {e}")

        return config

    async def _save_case_result(self, execution_id: str, result: ApiCaseResult):
        """保存用例执行结果"""
        step_dict = result.step.to_dict() if result.step else {}

        case_result = CaseResult(
            id=generate_id("result"),
            execution_id=execution_id,
            case_id=result.case_id,
            case_name=result.case_name,
            status=result.status,
            duration=result.duration,
            error_message=result.error,
            steps_result=step_dict,
        )

        self.db.add(case_result)

    async def _get_execution(self, execution_id: str) -> Optional[Execution]:
        """获取执行记录"""
        result = await self.db.execute(
            select(Execution).where(Execution.id == execution_id)
        )
        return result.scalar_one_or_none()

    async def _get_cases_by_ids(self, case_ids: List[str]) -> List[TestCase]:
        """获取指定ID的用例"""
        if not case_ids:
            return []
        result = await self.db.execute(
            select(TestCase).where(TestCase.id.in_(case_ids))
        )
        return list(result.scalars().all())

    # ==================== 结果查询 ====================

    async def get_execution_results(
        self,
        execution_id: str,
        skip: int = 0,
        limit: int = 50,
    ) -> Tuple[List[CaseResult], int]:
        """
        获取执行结果列表

        Args:
            execution_id: 执行ID
            skip: 跳过记录数
            limit: 返回记录数

        Returns:
            tuple: (结果列表, 总数)
        """
        conditions = [CaseResult.execution_id == execution_id]

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

        # 查询列表
        result = await self.db.execute(
            select(CaseResult)
            .where(*conditions)
            .order_by(CaseResult.created_at)
            .offset(skip)
            .limit(limit)
        )
        results = list(result.scalars().all())

        return results, total

    async def get_executions(
        self,
        skip: int = 0,
        limit: int = 20,
    ) -> Tuple[List[Execution], int]:
        """
        获取接口测试执行记录列表

        Args:
            skip: 跳过记录数
            limit: 返回记录数

        Returns:
            tuple: (执行记录列表, 总数)
        """
        conditions = [Execution.case_type == "api"]

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

        # 查询列表
        result = await self.db.execute(
            select(Execution)
            .where(*conditions)
            .order_by(desc(Execution.created_at))
            .offset(skip)
            .limit(limit)
        )
        executions = list(result.scalars().all())

        return executions, total