"""
测试配置文件
提供全局测试fixture和钩子函数
"""

import os
import sys
import time
from pathlib import Path
from typing import Generator, Optional
import pytest
from pytest import Config

from playwright.sync_api import Browser, BrowserContext, Page, Playwright
from playwright.sync_api import Error as PlaywrightError

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

from config.settings import get_settings
from config.browsers import get_browser_factory, BrowserConfigManager
from utils.logger import get_logger
from utils.report_generator import get_report_manager, TestResult, TestStatus
from utils.data_generator import get_test_data_generator


@pytest.fixture(scope="session")
def settings() -> Generator:
    """获取测试配置"""
    yield get_settings()


@pytest.fixture(scope="session")
def logger() -> Generator:
    """获取日志记录器"""
    yield get_logger()


@pytest.fixture(scope="session")
def test_data_generator() -> Generator:
    """获取测试数据生成器"""
    yield get_test_data_generator()


@pytest.fixture(scope="session")
def browser_factory() -> Generator:
    """获取浏览器工厂"""
    factory = get_browser_factory()
    yield factory


@pytest.fixture(scope="session")
def browser_context(
    browser_factory: BrowserConfigManager,
    settings
) -> Generator:
    """创建浏览器上下文"""
    browser, context, page = browser_factory.create_browser_session(
        browser_name=settings.default_browser,
        headless=settings.headless_mode
    )
    
    yield context, page
    
    # 清理（在会话结束时）
    try:
        page.close()
    except Exception:
        pass
    try:
        context.close()
    except Exception:
        pass
    try:
        browser_factory.close_browser()
    except Exception:
        pass


@pytest.fixture
def page(browser_context, settings) -> Generator:
    """创建页面"""
    context, page = browser_context
    
    # 设置默认超时
    page.set_default_timeout(settings.page_load_timeout)
    page.set_default_navigation_timeout(settings.page_load_timeout)
    
    yield page
    
    # 截图（如果测试失败）
    if hasattr(page, "_test_failed") and page._test_failed:
        test_name = getattr(pytest, "_test_name", "unknown")
        screenshots_dir = Path(settings.screenshots_dir)
        screenshots_dir.mkdir(parents=True, exist_ok=True)
        
        screenshot_path = screenshots_dir / f"{test_name}_failed.png"
        try:
            page.screenshot(path=str(screenshot_path))
            logger = get_logger()
            logger.error(f"失败截图已保存: {screenshot_path}")
        except Exception as e:
            logger = get_logger()
            logger.error(f"保存失败截图时出错: {e}")


@pytest.fixture
def home_page(page: Page, settings) -> Generator:
    """创建首页对象"""
    from pages.home_page import HomePage
    home = HomePage(page, settings.get_base_url())
    yield home


@pytest.fixture
def contact_page(page: Page, settings) -> Generator:
    """创建联系页面对象"""
    from pages.contact_page import ContactPage
    contact = ContactPage(page, settings.get_base_url())
    yield contact


@pytest.fixture(scope="session")
def base_url(settings) -> str:
    """获取基础URL"""
    return settings.get_base_url()


@pytest.fixture(scope="session")
def test_results() -> Generator:
    """收集测试结果"""
    results = []
    yield results


@pytest.fixture(scope="session")
def report_manager() -> Generator:
    """获取报告管理器"""
    manager = get_report_manager()
    yield manager


@pytest.fixture
def track_test_result(report_manager, test_results):
    """跟踪测试结果fixture"""
    from datetime import datetime
    import pytest
    
    class TestTracker:
        def __init__(self):
            self.start_time = None
            self.current_result = None
        
        def start_track(self, test_name: str, test_class: str = "", test_file: str = ""):
            self.start_time = datetime.now()
            logger = get_logger()
            logger.log_test_start(test_name, test_class=test_class, test_file=test_file)
        
        def end_track(
            self,
            test_name: str,
            status: TestStatus,
            test_class: str = "",
            test_file: str = ""
        ):
            end_time = datetime.now()
            duration = (end_time - self.start_time).total_seconds()
            
            logger = get_logger()
            logger.log_test_end(test_name, status.value, duration)
            
            # 创建测试结果
            result = TestResult(
                test_id=f"{test_file}_{test_name}",
                test_name=test_name,
                test_file=test_file,
                test_class=test_class,
                status=status,
                start_time=self.start_time,
                end_time=end_time,
                duration=duration
            )
            
            # 添加到报告管理器
            report_manager.add_result(result)
            test_results.append(result)
            
            return result
    
    tracker = TestTracker()
    yield tracker


# Pytest钩子函数

def pytest_configure(config: Config):
    """pytest配置钩子"""
    # 设置标记
    config.addinivalue_line(
        "markers", "smoke: 冒烟测试，快速验证核心功能"
    )
    config.addinivalue_line(
        "markers", "regression: 回归测试，完整功能验证"
    )
    config.addinivalue_line(
        "markers", "performance: 性能测试，页面加载和响应时间"
    )
    config.addinivalue_line(
        "markers", "responsive: 响应式测试，不同屏幕尺寸"
    )
    config.addinivalue_line(
        "markers", "cross_browser: 跨浏览器测试"
    )
    config.addinivalue_line(
        "markers", "form: 表单相关测试"
    )
    config.addinivalue_line(
        "markers", "navigation: 导航测试"
    )
    config.addinivalue_line(
        "markers", "interactive: 用户交互测试"
    )
    
    # 创建必要的目录
    settings = get_settings()
    Path(settings.screenshots_dir).mkdir(parents=True, exist_ok=True)
    Path(settings.videos_dir).mkdir(parents=True, exist_ok=True)
    Path("reports").mkdir(parents=True, exist_ok=True)


def pytest_sessionstart(session):
    """测试会话开始"""
    logger = get_logger()
    logger.section("开始E2E测试会话")
    logger.info(f"测试会话ID: {session.name}")
    logger.info(f"测试数量: {len(session.items)}")


def pytest_sessionfinish(session, exitstatus):
    """测试会话结束"""
    logger = get_logger()
    logger.section("E2E测试会话结束")
    
    # 生成报告
    if exitstatus == 0:
        logger.info("✅ 所有测试通过")
    else:
        logger.warning(f"⚠️ 测试完成，退出码: {exitstatus}")


def pytest_runtest_setup(item):
    """测试运行前设置"""
    logger = get_logger()
    logger.divider()
    logger.log_test_start(item.name)


def pytest_runtest_makereport(item, call):
    """生成测试报告"""
    if call.when == "call":
        if call.excinfo:
            item._test_failed = True
        else:
            item._test_failed = False


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_call(item):
    """测试运行钩子"""
    yield
    # 测试运行后处理


def pytest_collection_modifyitems(config, items):
    """修改测试项目"""
    # 按标记排序
    marker_priority = {
        "smoke": 0,
        "performance": 1,
        "regression": 2,
        "responsive": 3,
        "cross_browser": 4,
        "form": 5,
        "navigation": 6,
        "interactive": 7
    }
    
    def get_priority(item):
        for marker in item.iter_markers():
            if marker.name in marker_priority:
                return marker_priority[marker.name]
        return 10
    
    items.sort(key=get_priority)


# 自定义断言

class E2EAssertions:
    """E2E测试断言类"""
    
    def __init__(self, page: Page):
        self.page = page
        self.logger = get_logger()
    
    def title_contains(self, expected: str, message: Optional[str] = None) -> None:
        """断言标题包含预期文本"""
        actual = self.page.title()
        assert expected in actual, message or f"标题不包含 '{expected}': {actual}"
        self.logger.log_assertion(f"标题包含 '{expected}'", True)
    
    def url_contains(self, expected: str, message: Optional[str] = None) -> None:
        """断言URL包含预期文本"""
        assert expected in self.page.url, message or f"URL不包含 '{expected}': {self.page.url}"
        self.logger.log_assertion(f"URL包含 '{expected}'", True)
    
    def element_exists(self, selector: str, timeout: int = 5000) -> None:
        """断言元素存在"""
        try:
            self.page.wait_for_selector(selector, timeout=timeout)
            self.logger.log_assertion(f"元素存在: {selector}", True)
        except Exception as e:
            self.logger.log_assertion(f"元素存在: {selector}", False)
            raise AssertionError(f"元素不存在: {selector}") from e
    
    def element_visible(self, selector: str, timeout: int = 5000) -> None:
        """断言元素可见"""
        element = self.page.locator(selector).first
        assert element.is_visible(timeout=timeout), f"元素不可见: {selector}"
        self.logger.log_assertion(f"元素可见: {selector}", True)
    
    def element_not_visible(self, selector: str, timeout: int = 5000) -> None:
        """断言元素不可见"""
        element = self.page.locator(selector).first
        assert not element.is_visible(timeout=timeout), f"元素应该不可见: {selector}"
        self.logger.log_assertion(f"元素不可见: {selector}", True)


@pytest.fixture
def assert_(page: Page) -> E2EAssertions:
    """断言fixture"""
    return E2EAssertions(page)
