#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
元素映射表服务单元测试

验证 ElementMappingService 的三种匹配策略：
- 精确匹配评分：解决"会议预约"歧义（应匹配筛选器-会议预约 而非 会议服务统计）
- 模糊匹配（jieba + SequenceMatcher ≥0.7）
- 上下文辅助匹配
- 容错：JSON 缺失时自动禁用
"""

import json
import os
import sys
import tempfile

import pytest

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "app"))

from app.services.element_mapping_service import ElementMappingService


@pytest.fixture
def sample_mapping(tmp_path):
    """创建一份最小映射表用于测试（避免依赖完整 JSON）"""
    data = {
        "version": "1.0",
        "updated_at": "2026-08-11",
        "source": "test",
        "elements": {
            "导航栏-功能中心抽屉开关": {"selector": ".home_nav_left", "type": "css", "tier": "首页导航栏", "description": "打开功能中心抽屉"},
            "全部功能-筛选器-会议预约": {"selector": "#reserve_enable", "type": "css", "tier": "功能中心-筛选器", "description": "会议预约"},
            "全部功能-筛选器-数据统计": {"selector": "#data_enable", "type": "css", "tier": "功能中心-筛选器", "description": "数据统计"},
            "首页-常用功能-创建会议": {"selector": '[data-key="reserve_list.create"]', "type": "css", "tier": "首页功能块", "description": "创建会议"},
            "首页-常用功能-会议室": {"selector": '[data-key="reserve_list.room"]', "type": "css", "tier": "首页功能块", "description": "会议室"},
            "首页-常用功能-会议列表": {"selector": '[data-key="reserve_list.meeting"]', "type": "css", "tier": "首页功能块", "description": "会议列表"},
            "全部功能-数据统计-会议服务统计": {"selector": '[data-id="data_list.meeting_service"]', "type": "css", "tier": "功能中心-全部功能", "description": "会议服务统计"},
            "全部功能-数据统计-会议统计": {"selector": '[data-id="data_list.meeting_total"]', "type": "css", "tier": "功能中心-全部功能", "description": "会议统计"},
            "功能中心-搜索输入框": {"selector": ".menu_header .search input", "type": "css", "tier": "功能中心-搜索框", "description": "功能中心搜索框"},
        },
    }
    mapping_file = tmp_path / "elements_mapping.json"
    mapping_file.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
    return str(mapping_file)


def _make_service(mapping_file):
    return ElementMappingService(mapping_file)


class TestExactMatch:
    """精确匹配测试 - 评分公式"""

    def test_classification_meeting_preference(self, sample_mapping):
        """'会议预约'分类应匹配筛选器-会议预约，而非会议服务统计"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("点击【会议预约】分类", "click")
        assert result is not None
        assert result["selector"] == "#reserve_enable"

    def test_click_meeting_service_preference(self, sample_mapping):
        """'会议服务统计'应匹配对应统计项"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("点击【会议服务统计】", "click")
        assert result is not None
        assert result["selector"] == '[data-id="data_list.meeting_service"]'

    def test_create_meeting(self, sample_mapping):
        """'新建会议'应通过同义词匹配到创建会议"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("点击【新建会议】按钮", "click")
        assert result is not None
        assert result["selector"] == '[data-key="reserve_list.create"]'

    def test_drawer(self, sample_mapping):
        """'功能中心抽屉'应匹配导航栏开关"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("点击功能中心抽屉展开", "click")
        assert result is not None
        assert result["selector"] == ".home_nav_left"

    def test_search(self, sample_mapping):
        """搜索输入框匹配"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("输入搜索关键词", "fill")
        assert result is not None
        assert result["selector"] == ".menu_header .search input"


class TestFaultTolerance:
    """容错测试"""

    def test_missing_file(self, tmp_path):
        """映射表文件不存在时，服务禁用但不抛异常"""
        svc = ElementMappingService(str(tmp_path / "nonexistent.json"))
        assert not svc.is_enabled()
        assert svc.get_selector("点击【会议预约】分类", "click") is None

    def test_invalid_json(self, tmp_path):
        """映射表 JSON 损坏时，服务禁用"""
        bad_file = tmp_path / "bad.json"
        bad_file.write_text("{invalid json", encoding="utf-8")
        svc = ElementMappingService(str(bad_file))
        assert not svc.is_enabled()

    def test_empty_elements(self, tmp_path):
        """elements 为空时禁用"""
        empty_file = tmp_path / "empty.json"
        empty_file.write_text(json.dumps({"elements": {}}, ensure_ascii=False), encoding="utf-8")
        svc = ElementMappingService(str(empty_file))
        assert not svc.is_enabled()
        assert svc.get_selector("点击任意", "click") is None


class TestNoMatch:
    """未命中时返回 None"""

    def test_unrelated_step(self, sample_mapping):
        """映射表未覆盖的步骤返回 None"""
        svc = _make_service(sample_mapping)
        result = svc.get_selector("删除某条记录", "click")
        assert result is None


if __name__ == "__main__":
    sys.exit(pytest.main(["-v", __file__]))