"""
导出服务单元测试

测试导出服务的各项功能：
- JSON导出
- CSV导出
- 批量导出
- 文件管理
"""

import sys
import os
import pytest
import json
import csv
from datetime import datetime

# 将项目根目录添加到 sys.path
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
if project_root not in sys.path:
    sys.path.insert(0, project_root)

from src.core.config import get_config
from src.core.logger import get_logger, setup_logger
from src.services.export_service import ExportService


@pytest.fixture
def temp_export_dir(tmp_path):
    """
    创建临时导出目录
    """
    return str(tmp_path / "exports")


@pytest.fixture
def export_service(temp_export_dir):
    """
    创建导出服务实例
    """
    config = get_config()
    
    # 修改导出目录为临时目录
    original_get = config.get
    def patched_get(key, default=None):
        if key == "export.directory":
            return temp_export_dir
        return original_get(key, default)
    
    config.get = patched_get
    logger = setup_logger(config)

    service = ExportService(config=config, logger=logger)
    return service


@pytest.fixture
def sample_data():
    """
    创建示例数据
    """
    return {
        'total_questions': 100,
        'published_questions': 80,
        'draft_questions': 15,
        'archived_questions': 5,
        'average_points': 10.5,
        'total_points': 1050,
        'total_tags': 25,
        'total_categories': 5
    }


@pytest.fixture
def sample_csv_data():
    """
    创建示例CSV数据
    """
    return [
        {'question_id': '1', 'title': '题目1', 'category': '数学', 'difficulty': '简单'},
        {'question_id': '2', 'title': '题目2', 'category': 'Python', 'difficulty': '中等'},
        {'question_id': '3', 'title': '题目3', 'category': '英语', 'difficulty': '困难'},
    ]


class TestJsonExport:
    """JSON导出测试"""

    def test_export_to_json_with_filename(self, export_service, sample_data):
        """测试导出JSON到文件"""
        result = export_service.export_to_json(
            data=sample_data,
            filename="test_statistics.json"
        )

        assert result['status'] == 'success'
        assert result['format'] == 'json'
        assert 'filepath' in result
        assert result['records_count'] == 1
        assert os.path.exists(result['filepath'])

        # 验证文件内容
        with open(result['filepath'], 'r', encoding='utf-8') as f:
            loaded_data = json.load(f)
            assert loaded_data['total_questions'] == 100

    def test_export_to_json_without_filename(self, export_service, sample_data):
        """测试导出JSON到字符串"""
        result = export_service.export_to_json(
            data=sample_data,
            filename=None
        )

        assert result['status'] == 'success'
        assert 'data' in result
        assert 'data_size' in result
        
        # 验证JSON字符串
        loaded_data = json.loads(result['data'])
        assert loaded_data['total_questions'] == 100

    def test_export_to_json_pretty_format(self, export_service, sample_data):
        """测试JSON美化输出"""
        result_pretty = export_service.export_to_json(
            data=sample_data,
            filename=None,
            pretty=True
        )

        result_compact = export_service.export_to_json(
            data=sample_data,
            filename=None,
            pretty=False
        )

        # 美化格式应该更长
        assert len(result_pretty['data']) >= len(result_compact['data'])

    def test_export_to_json_list_data(self, export_service):
        """测试导出JSON列表数据"""
        data = [
            {'id': 1, 'name': 'item1'},
            {'id': 2, 'name': 'item2'},
            {'id': 3, 'name': 'item3'},
        ]

        result = export_service.export_to_json(
            data=data,
            filename="test_list.json"
        )

        assert result['records_count'] == 3
        assert os.path.exists(result['filepath'])


class TestCsvExport:
    """CSV导出测试"""

    def test_export_to_csv_with_filename(self, export_service, sample_csv_data):
        """测试导出CSV到文件"""
        result = export_service.export_to_csv(
            data=sample_csv_data,
            filename="test_data.csv"
        )

        assert result['status'] == 'success'
        assert result['format'] == 'csv'
        assert result['records_count'] == 3
        assert result['columns_count'] == 4
        assert os.path.exists(result['filepath'])

        # 验证文件内容
        with open(result['filepath'], 'r', encoding='utf-8-sig') as f:
            reader = csv.DictReader(f)
            rows = list(reader)
            assert len(rows) == 3
            assert rows[0]['title'] == '题目1'

    def test_export_to_csv_without_filename(self, export_service, sample_csv_data):
        """测试导出CSV到字符串"""
        result = export_service.export_to_csv(
            data=sample_csv_data,
            filename=None
        )

        assert result['status'] == 'success'
        assert 'data' in result
        assert result['records_count'] == 3

        # 验证CSV内容
        lines = result['data'].strip().split('\n')
        assert len(lines) == 4  # 1个表头 + 3行数据

    def test_export_to_csv_without_headers(self, export_service, sample_csv_data):
        """测试导出CSV不包含表头"""
        result = export_service.export_to_csv(
            data=sample_csv_data,
            filename=None,
            include_headers=False
        )

        lines = result['data'].strip().split('\n')
        assert len(lines) == 3  # 只有3行数据，没有表头

    def test_export_to_csv_empty_data(self, export_service):
        """测试导出空CSV数据"""
        with pytest.raises(ValueError):
            export_service.export_to_csv(data=[], filename="empty.csv")

    def test_export_to_csv_different_columns(self, export_service):
        """测试CSV中字段不统一的数据"""
        data = [
            {'id': 1, 'name': 'item1', 'category': 'A'},
            {'id': 2, 'name': 'item2'},  # 缺少category
            {'id': 3, 'value': 'item3'},  # 不同字段
        ]

        result = export_service.export_to_csv(
            data=data,
            filename="mixed_columns.csv"
        )

        assert result['status'] == 'success'
        assert os.path.exists(result['filepath'])


class TestBatchExport:
    """批量导出测试"""

    def test_export_overall_statistics(self, export_service, sample_data):
        """测试导出整体统计"""
        result = export_service.export_overall_statistics(
            data=sample_data,
            formats=['json', 'csv'],
            base_filename='overall_stats'
        )

        assert result['status'] == 'success'
        assert 'json' in result['exports']
        assert 'csv' in result['exports']
        assert result['formats'] == ['json', 'csv']

        # 验证文件存在
        assert os.path.exists(result['exports']['json']['filepath'])
        assert os.path.exists(result['exports']['csv']['filepath'])

    def test_export_category_statistics(self, export_service):
        """测试导出分类统计"""
        category_data = {
            'total_categories': 2,
            'categories': [
                {'category': '数学', 'total_count': 50, 'easy_count': 20},
                {'category': 'Python', 'total_count': 30, 'easy_count': 10}
            ]
        }

        result = export_service.export_category_statistics(
            data=category_data,
            formats=['json', 'csv'],
            base_filename='category_stats'
        )

        assert result['status'] == 'success'
        assert 'json' in result['exports']

    def test_export_quality_metrics(self, export_service):
        """测试导出质量评估"""
        quality_data = {
            'total_questions_evaluated': 3,
            'average_quality_score': 75.5,
            'metrics': [
                {'question_id': '1', 'quality_score': 80.0},
                {'question_id': '2', 'quality_score': 75.0},
                {'question_id': '3', 'quality_score': 71.0},
            ]
        }

        result = export_service.export_quality_metrics(
            data=quality_data,
            formats=['json', 'csv'],
            base_filename='quality_metrics'
        )

        assert result['status'] == 'success'
        assert 'json' in result['exports']

    def test_export_time_series(self, export_service):
        """测试导出时间序列"""
        time_series_data = {
            'period': 'day',
            'days': 30,
            'time_series': [
                {
                    'timestamp': '2024-01-01',
                    'questions_created': 5,
                    'cumulative_total': 5
                },
                {
                    'timestamp': '2024-01-02',
                    'questions_created': 3,
                    'cumulative_total': 8
                }
            ]
        }

        result = export_service.export_time_series(
            data=time_series_data,
            formats=['json', 'csv'],
            base_filename='time_series'
        )

        assert result['status'] == 'success'


class TestFileManagement:
    """文件管理测试"""

    def test_list_export_files(self, export_service, sample_data):
        """测试列出导出文件"""
        # 先导出一些文件
        export_service.export_to_json(sample_data, "test1.json")
        export_service.export_to_json(sample_data, "test2.json")

        # 列出文件
        result = export_service.list_export_files()

        assert result['status'] == 'success'
        assert result['total_files'] == 2
        assert len(result['files']) == 2
        assert any(f['filename'] == 'test1.json' for f in result['files'])
        assert any(f['filename'] == 'test2.json' for f in result['files'])

    def test_list_export_files_empty(self, export_service):
        """测试列出空导出文件列表"""
        result = export_service.list_export_files()

        assert result['status'] == 'success'
        assert result['total_files'] == 0

    def test_delete_export_file(self, export_service, sample_data):
        """测试删除导出文件"""
        # 先导出一个文件
        export_service.export_to_json(sample_data, "test_delete.json")

        # 验证文件存在
        files = export_service.list_export_files()
        assert files['total_files'] == 1

        # 删除文件
        result = export_service.delete_export_file("test_delete.json")

        assert result['status'] == 'success'

        # 验证文件已删除
        files = export_service.list_export_files()
        assert files['total_files'] == 0

    def test_delete_nonexistent_file(self, export_service):
        """测试删除不存在的文件"""
        with pytest.raises(FileNotFoundError):
            export_service.delete_export_file("nonexistent.json")

    def test_delete_file_path_traversal_protection(self, export_service):
        """测试路径遍历攻击防护"""
        with pytest.raises(ValueError):
            export_service.delete_export_file("../../etc/passwd")

    def test_cleanup_old_exports(self, export_service, sample_data, tmp_path):
        """测试清理旧文件"""
        # 导出多个文件
        export_service.export_to_json(sample_data, "test1.json")
        export_service.export_to_json(sample_data, "test2.json")

        # 验证文件存在
        files = export_service.list_export_files()
        assert files['total_files'] == 2

        # 清理（应该不删除，因为文件都是新的）
        result = export_service.cleanup_old_exports(days=0)

        assert result['status'] == 'success'
        # 由于文件是刚创建的，应该被删除
        assert result['deleted_count'] >= 0


class TestExportErrorHandling:
    """错误处理测试"""

    def test_export_with_special_characters(self, export_service):
        """测试导出包含特殊字符的数据"""
        data = {
            'title': '题目-中文-特殊字符！@#$%',
            'content': '内容\n换行\t制表符',
            'emoji': '😊🎉🚀'
        }

        result = export_service.export_to_json(data, "special_chars.json")

        assert result['status'] == 'success'
        assert os.path.exists(result['filepath'])

        # 验证内容
        with open(result['filepath'], 'r', encoding='utf-8') as f:
            loaded = json.load(f)
            assert '中文' in loaded['title']
            assert '😊' in loaded['emoji']

    def test_export_large_data(self, export_service):
        """测试导出大型数据"""
        # 创建包含1000条记录的数据
        data = [
            {'id': i, 'value': f'item_{i}', 'score': i * 0.1}
            for i in range(1000)
        ]

        result = export_service.export_to_csv(data, "large_data.csv")

        assert result['status'] == 'success'
        assert result['records_count'] == 1000

    def test_export_nested_structure(self, export_service):
        """测试导出嵌套结构数据"""
        data = {
            'level1': {
                'level2': {
                    'level3': [1, 2, 3]
                }
            },
            'timestamp': datetime.now()
        }

        result = export_service.export_to_json(data, "nested.json")

        assert result['status'] == 'success'
        assert os.path.exists(result['filepath'])

        # 验证默认的datetime转换
        with open(result['filepath'], 'r', encoding='utf-8') as f:
            content = f.read()
            assert 'level1' in content


class TestExportIntegration:
    """集成测试"""

    def test_full_export_workflow(self, export_service, sample_data, sample_csv_data):
        """测试完整导出工作流"""
        # 导出JSON
        json_result = export_service.export_to_json(sample_data, "workflow_stats.json")
        assert json_result['status'] == 'success'

        # 导出CSV
        csv_result = export_service.export_to_csv(sample_csv_data, "workflow_data.csv")
        assert csv_result['status'] == 'success'

        # 列出文件
        files = export_service.list_export_files()
        assert files['total_files'] == 2

        # 验证文件内容
        with open(json_result['filepath'], 'r', encoding='utf-8') as f:
            json_data = json.load(f)
            assert json_data == sample_data

        with open(csv_result['filepath'], 'r', encoding='utf-8-sig') as f:
            reader = csv.DictReader(f)
            csv_rows = list(reader)
            assert len(csv_rows) == len(sample_csv_data)

    def test_multiple_batch_exports(self, export_service):
        """测试多个批量导出"""
        data1 = {'name': 'export1', 'value': 100}
        data2 = {'name': 'export2', 'value': 200}

        result1 = export_service.export_overall_statistics(
            data=data1,
            formats=['json'],
            base_filename='batch1'
        )

        result2 = export_service.export_overall_statistics(
            data=data2,
            formats=['json'],
            base_filename='batch2'
        )

        files = export_service.list_export_files()
        assert files['total_files'] == 2


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