"""用例执行引擎"""

import time
import traceback
from typing import Dict, Any, Optional, List, Callable
from datetime import datetime
from pathlib import Path

from .read.excel_reader import TestCase, TestStep
from .read.cell_handler import CellHandler
from ..action.key_retrieval import KeyRetrieval
from ..reference import (
    GSTORE, PRIVATEDATA, CASESDATA, INFO,
    set_element_value, clear_element_values
)


class StepResult:
    """步骤执行结果"""

    def __init__(self, step: TestStep):
        self.step = step
        self.status: str = 'pending'  # pending, passed, failed, skipped
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None
        self.duration: float = 0
        self.error: Optional[str] = None
        self.screenshot: Optional[str] = None

    @property
    def is_passed(self) -> bool:
        return self.status == 'passed'

    @property
    def is_failed(self) -> bool:
        return self.status == 'failed'


class CaseResult:
    """用例执行结果"""

    def __init__(self, case: TestCase):
        self.case = case
        self.status: str = 'pending'  # pending, passed, failed, skipped
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None
        self.duration: float = 0
        self.step_results: List[StepResult] = []
        self.error: Optional[str] = None

    @property
    def is_passed(self) -> bool:
        return self.status == 'passed'

    @property
    def is_failed(self) -> bool:
        return self.status == 'failed'

    @property
    def passed_steps(self) -> int:
        return sum(1 for s in self.step_results if s.is_passed)

    @property
    def failed_steps(self) -> int:
        return sum(1 for s in self.step_results if s.is_failed)


class CaseExecutor:
    """用例执行引擎

    执行测试用例，管理执行流程和结果收集。
    """

    def __init__(
        self,
        key_retrieval: KeyRetrieval = None,
        cell_handler: CellHandler = None,
        retry_count: int = 0,
        screenshot_on_fail: bool = True,
        stop_on_fail: bool = False
    ):
        """初始化执行引擎

        Args:
            key_retrieval: 关键字分发器
            cell_handler: 单元格处理器
            retry_count: 失败重试次数
            screenshot_on_fail: 失败时是否截图
            stop_on_fail: 失败时是否停止
        """
        self._key_retrieval = key_retrieval or KeyRetrieval()
        self._cell_handler = cell_handler or CellHandler()
        self._retry_count = retry_count
        self._screenshot_on_fail = screenshot_on_fail
        self._stop_on_fail = stop_on_fail

        self._results: List[CaseResult] = []
        self._current_case: Optional[TestCase] = None
        self._current_result: Optional[CaseResult] = None

        # 回调
        self._on_case_start: Optional[Callable] = None
        self._on_case_end: Optional[Callable] = None
        self._on_step_start: Optional[Callable] = None
        self._on_step_end: Optional[Callable] = None

    def set_callbacks(
        self,
        on_case_start: Callable = None,
        on_case_end: Callable = None,
        on_step_start: Callable = None,
        on_step_end: Callable = None
    ) -> None:
        """设置回调函数

        Args:
            on_case_start: 用例开始回调
            on_case_end: 用例结束回调
            on_step_start: 步骤开始回调
            on_step_end: 步骤结束回调
        """
        self._on_case_start = on_case_start
        self._on_case_end = on_case_end
        self._on_step_start = on_step_start
        self._on_step_end = on_step_end

    def execute_cases(self, cases: List[TestCase]) -> List[CaseResult]:
        """执行多个用例

        Args:
            cases: 用例列表

        Returns:
            List[CaseResult]: 执行结果列表
        """
        self._results.clear()
        CASESDATA['total'] = len(cases)
        CASESDATA['passed'] = 0
        CASESDATA['failed'] = 0
        CASESDATA['skipped'] = 0

        INFO(f"开始执行 {len(cases)} 个用例")

        for case in cases:
            result = self.execute_case(case)
            self._results.append(result)

            if result.is_passed:
                CASESDATA['passed'] += 1
            elif result.is_failed:
                CASESDATA['failed'] += 1
                if self._stop_on_fail:
                    INFO("用例失败，停止执行", "WARNING")
                    break
            else:
                CASESDATA['skipped'] += 1

        INFO(f"执行完成: 通过 {CASESDATA['passed']}, 失败 {CASESDATA['failed']}, 跳过 {CASESDATA['skipped']}")
        return self._results

    def execute_case(self, case: TestCase) -> CaseResult:
        """执行单个用例

        Args:
            case: 测试用例

        Returns:
            CaseResult: 执行结果
        """
        self._current_case = case
        self._current_result = CaseResult(case)
        PRIVATEDATA['CURRENT_CASE'] = case.name

        INFO(f"执行用例: [{case.id}] {case.name}")

        # 回调
        if self._on_case_start:
            self._on_case_start(case)

        self._current_result.start_time = datetime.now()

        # 清除上一个用例的缓存
        clear_element_values()

        try:
            # 执行所有步骤
            for step in case.steps:
                step_result = self._execute_step_with_retry(step)
                self._current_result.step_results.append(step_result)

                if step_result.is_failed and self._stop_on_fail:
                    break

            # 判断用例结果
            if any(s.is_failed for s in self._current_result.step_results):
                self._current_result.status = 'failed'
            else:
                self._current_result.status = 'passed'

        except Exception as e:
            self._current_result.status = 'failed'
            self._current_result.error = str(e)
            INFO(f"用例执行异常: {e}", "ERROR")

        self._current_result.end_time = datetime.now()
        self._current_result.duration = (
            self._current_result.end_time - self._current_result.start_time
        ).total_seconds()

        status_text = '通过' if self._current_result.is_passed else '失败'
        INFO(f"用例完成: [{case.id}] {case.name} - {status_text} ({self._current_result.duration:.2f}s)")

        # 回调
        if self._on_case_end:
            self._on_case_end(case, self._current_result)

        return self._current_result

    def _execute_step_with_retry(self, step: TestStep) -> StepResult:
        """执行步骤（带重试）

        Args:
            step: 测试步骤

        Returns:
            StepResult: 步骤结果
        """
        result = None
        retry = 0

        while retry <= self._retry_count:
            result = self._execute_step(step)

            if result.is_passed:
                break

            if retry < self._retry_count:
                INFO(f"步骤失败，第 {retry + 1} 次重试...", "WARNING")
                retry += 1
            else:
                break

        return result

    def _execute_step(self, step: TestStep) -> StepResult:
        """执行单个步骤

        Args:
            step: 测试步骤

        Returns:
            StepResult: 步骤结果
        """
        result = StepResult(step)

        # 回调
        if self._on_step_start:
            self._on_step_start(step)

        result.start_time = datetime.now()

        try:
            # 处理定位信息
            locator_info = []
            for loc in step.locator:
                processed = self._cell_handler.process(loc)
                if processed:
                    locator_info.append(processed)

            # 处理操作值
            value = self._cell_handler.process(step.value) if step.value else None

            # 执行关键字
            INFO(f"  步骤: {step.description or step.keyword}")
            exec_result = self._key_retrieval.execute(
                keyword=step.keyword,
                locator_info=locator_info,
                value=value
            )

            # 处理断言关键字返回值
            if step.keyword.endswith('_assert') or step.keyword.startswith('assert'):
                if exec_result is False:
                    raise AssertionError(f"断言失败: {step.keyword}")

            result.status = 'passed'

        except Exception as e:
            result.status = 'failed'
            result.error = str(e)
            INFO(f"  步骤失败: {e}", "ERROR")

            # 失败截图
            if self._screenshot_on_fail:
                result.screenshot = self._take_screenshot(step)

        result.end_time = datetime.now()
        result.duration = (result.end_time - result.start_time).total_seconds()

        # 回调
        if self._on_step_end:
            self._on_step_end(step, result)

        return result

    def _take_screenshot(self, step: TestStep) -> Optional[str]:
        """失败截图

        Args:
            step: 测试步骤

        Returns:
            str: 截图路径
        """
        try:
            page = GSTORE.get('page')
            if not page:
                return None

            # 生成截图文件名
            timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
            case_name = self._current_case.name if self._current_case else 'unknown'
            # 移除非法字符
            case_name = ''.join(c for c in case_name if c.isalnum() or c in ('_', '-'))

            screenshot_dir = Path('result/screenshot')
            screenshot_dir.mkdir(parents=True, exist_ok=True)

            screenshot_path = screenshot_dir / f"{case_name}_{step.row}_{timestamp}.png"
            page.screenshot(path=str(screenshot_path))

            INFO(f"  截图已保存: {screenshot_path}")
            return str(screenshot_path)

        except Exception as e:
            INFO(f"  截图失败: {e}", "WARNING")
            return None

    @property
    def results(self) -> List[CaseResult]:
        """获取所有执行结果"""
        return self._results.copy()

    @property
    def summary(self) -> Dict[str, Any]:
        """获取执行摘要"""
        total = len(self._results)
        passed = sum(1 for r in self._results if r.is_passed)
        failed = sum(1 for r in self._results if r.is_failed)
        duration = sum(r.duration for r in self._results)

        return {
            'total': total,
            'passed': passed,
            'failed': failed,
            'skipped': total - passed - failed,
            'pass_rate': (passed / total * 100) if total > 0 else 0,
            'duration': duration,
        }
