"""StrEnum 反序列化单元测试。

验证 SQLAlchemy 仓储从 DB 读取数据后，实体的 status 字段是正确的 StrEnum 类型，
而不是纯字符串。这是 500 错误的根因修复测试。

根因：SQLAlchemy Column(String(20)) 读出的是 str，直接传给 domain 实体后，
路由层 job.status.value 会抛 AttributeError（str 没有 .value 属性）。
"""

from __future__ import annotations

from datetime import datetime, timezone
from unittest.mock import MagicMock

import pytest

from packages.adapters.sqlalchemy_impl.tts_job_repository import SQLAlchemyTTSJobRepository
from packages.adapters.sqlalchemy_impl.voice_clone_profile_repository import SQLAlchemyVoiceCloneProfileRepository
from packages.domain.generation_task import GenerationTaskStatus
from packages.domain.tts_job import TTSJobStatus
from packages.domain.voice_clone_profile import VoiceCloneStatus


def _make_tts_model(status: str = "pending"):
    """模拟 SQLAlchemy TTSJobModel（status 是纯字符串）。"""
    model = MagicMock()
    model.id = "job_001"
    model.user_id = "user_001"
    model.input_text = "测试文本"
    model.voice_id = "voice_001"
    model.voice_model = ""
    model.project_id = ""
    model.voice_clone_profile_id = ""
    model.status = status  # 纯字符串，模拟 DB 读取
    model.output_audio_url = ""
    model.output_audio_key = ""
    model.duration = 0.0
    model.file_size = 0
    model.sample_rate = 22050
    model.format = "mp3"
    model.error_message = ""
    model.retry_count = 0
    model.max_retries = 3
    model.metadata_ = {}
    model.started_at = None
    model.completed_at = None
    model.created_at = datetime.now(timezone.utc)
    model.updated_at = datetime.now(timezone.utc)
    return model


def _make_voice_clone_model(status: str = "pending"):
    """模拟 SQLAlchemy VoiceCloneProfileModel（status 是纯字符串）。"""
    model = MagicMock()
    model.id = "profile_001"
    model.user_id = "user_001"
    model.name = "测试音色"
    model.description = ""
    model.source_audio_url = "https://example.com/audio.wav"
    model.voice_id = ""
    model.voice_model = ""
    model.language = "zh-CN"
    model.gender = "unknown"
    model.status = status  # 纯字符串，模拟 DB 读取
    model.error_message = ""
    model.retry_count = 0
    model.max_retries = 3
    model.metadata_ = {}
    model.created_at = datetime.now(timezone.utc)
    model.updated_at = datetime.now(timezone.utc)
    return model


def _make_generation_task_model(status: str = "pending"):
    """模拟 SQLAlchemy GenerationTaskModel（status 是纯字符串）。"""
    model = MagicMock()
    model.id = "task_001"
    model.project_id = "project_001"
    model.strategy_id = "strategy_001"
    model.asset_library_id = "lib_001"
    model.voice_library_id = "voice_lib_001"
    model.template_id = "template_001"
    model.asset_ids = []
    model.title_ids = []
    model.voice_ids = []
    model.status = status  # 纯字符串，模拟 DB 读取
    model.progress = 0.0
    model.result_count = 0
    model.error_message = ""
    model.started_at = None
    model.completed_at = None
    model.created_by_user_id = "user_001"
    model.source_edit_plan_id = ""
    model.created_at = datetime.now(timezone.utc)
    return model


class TestTTSJobStatusDeserialization:
    """TTSJob 仓储：DB 读出的 status 字符串应转为 TTSJobStatus 枚举。"""

    @pytest.mark.parametrize("status_str", ["pending", "processing", "completed", "failed", "cancelled"])
    def test_model_to_entity_converts_status_to_enum(self, status_str: str) -> None:
        """_model_to_entity 将纯字符串 status 转为 TTSJobStatus 枚举。"""
        model = _make_tts_model(status=status_str)
        entity = SQLAlchemyTTSJobRepository._model_to_entity(model)

        assert isinstance(
            entity.status, TTSJobStatus
        ), f"status 应该是 TTSJobStatus 枚举，实际是 {type(entity.status).__name__}"
        assert entity.status.value == status_str

    def test_status_value_attribute_works(self) -> None:
        """路由层 job.status.value 不再抛 AttributeError。"""
        model = _make_tts_model(status="processing")
        entity = SQLAlchemyTTSJobRepository._model_to_entity(model)

        # 这是之前 500 错误的直接原因
        assert entity.status.value == "processing"

    def test_status_string_comparison_still_works(self) -> None:
        """StrEnum 继承自 str，字符串比较仍然兼容。"""
        model = _make_tts_model(status="completed")
        entity = SQLAlchemyTTSJobRepository._model_to_entity(model)

        assert entity.status == "completed"  # StrEnum 与 str 可比较
        assert entity.status == TTSJobStatus.COMPLETED


class TestVoiceCloneStatusDeserialization:
    """VoiceCloneProfile 仓储：DB 读出的 status 字符串应转为 VoiceCloneStatus 枚举。"""

    @pytest.mark.parametrize("status_str", ["pending", "processing", "ready", "failed", "disabled"])
    def test_model_to_entity_converts_status_to_enum(self, status_str: str) -> None:
        """_model_to_entity 将纯字符串 status 转为 VoiceCloneStatus 枚举。"""
        model = _make_voice_clone_model(status=status_str)
        entity = SQLAlchemyVoiceCloneProfileRepository._model_to_entity(model)

        assert isinstance(
            entity.status, VoiceCloneStatus
        ), f"status 应该是 VoiceCloneStatus 枚举，实际是 {type(entity.status).__name__}"
        assert entity.status.value == status_str

    def test_status_value_attribute_works(self) -> None:
        """profile.status.value 不再抛 AttributeError。"""
        model = _make_voice_clone_model(status="processing")
        entity = SQLAlchemyVoiceCloneProfileRepository._model_to_entity(model)

        assert entity.status.value == "processing"


class TestGenerationTaskStatusDeserialization:
    """GenerationTask 仓储：DB 读出的 status 字符串应转为 GenerationTaskStatus 枚举。"""

    @pytest.mark.parametrize("status_str", ["pending", "running", "completed", "failed", "cancelled"])
    def test_to_domain_converts_status_to_enum(self, status_str: str) -> None:
        """_to_domain 将纯字符串 status 转为 GenerationTaskStatus 枚举。"""
        model = _make_generation_task_model(status=status_str)

        # _to_domain 是模块级函数，直接导入使用
        from packages.adapters.sqlalchemy_impl.generation_task_repository import _to_domain

        entity = _to_domain(model)

        assert isinstance(
            entity.status, GenerationTaskStatus
        ), f"status 应该是 GenerationTaskStatus 枚举，实际是 {type(entity.status).__name__}"
        assert entity.status.value == status_str

    def test_status_value_attribute_works(self) -> None:
        """task.status.value 不再抛 AttributeError。"""
        model = _make_generation_task_model(status="running")

        from packages.adapters.sqlalchemy_impl.generation_task_repository import _to_domain

        entity = _to_domain(model)

        assert entity.status.value == "running"


class TestAlreadyCorrectRepositories:
    """验证已经正确转换枚举的仓储（回归测试，防止退化）。"""

    def test_edit_plan_status_converted(self) -> None:
        """EditPlan 仓储已正确转换 status。"""
        from packages.adapters.sqlalchemy_impl.edit_plan_repository import SQLAlchemyEditPlanRepository
        from packages.domain.edit_plan import EditPlanStatus

        model = MagicMock()
        model.id = "plan_001"
        model.template_id = "template_001"
        model.name = "测试计划"
        model.status = "editing"  # 纯字符串
        model.total_duration = 0.0
        model.source_edit_plan_id = ""
        model.project_id = ""
        model.created_by_user_id = "user_001"
        model.config = {}
        model.created_at = datetime.now(timezone.utc)
        model.updated_at = datetime.now(timezone.utc)

        entity = SQLAlchemyEditPlanRepository._model_to_entity(model)
        assert isinstance(entity.status, EditPlanStatus)
        assert entity.status.value == "editing"

    def test_edit_template_status_converted(self) -> None:
        """EditTemplate 仓储已正确转换 status。"""
        from packages.adapters.sqlalchemy_impl.edit_template_repository import SQLAlchemyEditTemplateRepository
        from packages.domain.edit_template import EditTemplateStatus

        model = MagicMock()
        model.id = "template_001"
        model.name = "测试模板"
        model.description = ""
        model.template_type = "default"
        model.config = {}
        model.preview_url = ""
        model.sort_weight = 0
        model.status = "active"  # 纯字符串
        model.created_at = datetime.now(timezone.utc)
        model.updated_at = datetime.now(timezone.utc)

        entity = SQLAlchemyEditTemplateRepository._model_to_entity(model)
        assert isinstance(entity.status, EditTemplateStatus)
        assert entity.status.value == "active"

    def test_edit_plan_clip_status_converted(self) -> None:
        """EditPlanClip 仓储已正确转换 status。"""
        from packages.adapters.sqlalchemy_impl.edit_plan_clip_repository import SQLAlchemyEditPlanClipRepository
        from packages.domain.edit_plan_clip import EditPlanClipStatus

        model = MagicMock()
        model.id = "clip_001"
        model.plan_id = "plan_001"
        model.clip_type = "main"
        model.order = 0
        model.template_clip_config_id = ""
        model.asset_id = ""
        model.text_content = ""
        model.start_time = 0.0
        model.duration = 0.0
        model.transition_effect = "cut"
        model.status = "ready"  # 纯字符串
        model.config = {}
        model.created_at = datetime.now(timezone.utc)
        model.updated_at = datetime.now(timezone.utc)

        entity = SQLAlchemyEditPlanClipRepository._model_to_entity(model)
        assert isinstance(entity.status, EditPlanClipStatus)
        assert entity.status.value == "ready"
