"""剪辑计划领域模型单元测试."""

from __future__ import annotations

from datetime import datetime, timezone

import pytest

from packages.domain.edit_plan import EditPlan, EditPlanStatus


class TestEditPlanStatus:
    """EditPlanStatus 枚举测试."""

    def test_status_values(self):
        assert EditPlanStatus.DRAFT.value == "draft"
        assert EditPlanStatus.EDITING.value == "editing"
        assert EditPlanStatus.RENDERING.value == "rendering"
        assert EditPlanStatus.COMPLETED.value == "completed"
        assert EditPlanStatus.FAILED.value == "failed"

    def test_status_is_str(self):
        assert isinstance(EditPlanStatus.DRAFT, str)
        assert EditPlanStatus.DRAFT == "draft"


class TestEditPlanCreate:
    """创建剪辑计划测试."""

    def test_create_basic(self):
        plan = EditPlan.create(template_id="tpl_001", name="测试计划")
        assert plan.id
        assert len(plan.id) == 32  # uuid4 hex
        assert plan.template_id == "tpl_001"
        assert plan.name == "测试计划"
        assert plan.status == EditPlanStatus.DRAFT
        assert plan.total_duration == 0.0
        assert plan.config == {}
        assert plan.source_edit_plan_id == ""
        assert plan.project_id == ""
        assert plan.created_by_user_id == ""

    def test_create_with_all_fields(self):
        plan = EditPlan.create(
            template_id="tpl_001",
            name="完整测试计划",
            config={"key": "value"},
            total_duration=60.5,
            source_edit_plan_id="src_001",
            project_id="proj_001",
            created_by_user_id="user_001",
        )
        assert plan.name == "完整测试计划"
        assert plan.total_duration == 60.5
        assert plan.config == {"key": "value"}
        assert plan.source_edit_plan_id == "src_001"
        assert plan.project_id == "proj_001"
        assert plan.created_by_user_id == "user_001"

    def test_create_empty_name_raises(self):
        with pytest.raises(ValueError, match="名称不能为空"):
            EditPlan.create(template_id="tpl_001", name="")

    def test_create_whitespace_name_raises(self):
        with pytest.raises(ValueError, match="名称不能为空"):
            EditPlan.create(template_id="tpl_001", name="   ")

    def test_create_empty_template_id_raises(self):
        with pytest.raises(ValueError, match="template_id 不能为空"):
            EditPlan.create(template_id="", name="测试")

    def test_create_whitespace_template_id_raises(self):
        with pytest.raises(ValueError, match="template_id 不能为空"):
            EditPlan.create(template_id="   ", name="测试")

    def test_create_name_stripped(self):
        plan = EditPlan.create(template_id="tpl_001", name="  我的计划  ")
        assert plan.name == "我的计划"

    def test_create_template_id_stripped(self):
        plan = EditPlan.create(template_id="  tpl_001  ", name="测试")
        assert plan.template_id == "tpl_001"

    def test_create_timestamps_set(self):
        before = datetime.now(timezone.utc)
        plan = EditPlan.create(template_id="tpl_001", name="测试")
        after = datetime.now(timezone.utc)
        assert before <= plan.created_at <= after
        assert before <= plan.updated_at <= after

    def test_create_config_none_defaults_to_empty(self):
        plan = EditPlan.create(template_id="tpl_001", name="测试", config=None)
        assert plan.config == {}


class TestEditPlanStateMachine:
    """状态机流转测试."""

    def _make_plan(self, status: EditPlanStatus) -> EditPlan:
        return EditPlan(
            id="test_id",
            template_id="tpl_001",
            name="测试计划",
            status=status,
        )

    def test_draft_to_editing(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        plan.start_editing()
        assert plan.status == EditPlanStatus.EDITING
        assert plan.updated_at > plan.created_at

    def test_editing_to_rendering(self):
        plan = self._make_plan(EditPlanStatus.EDITING)
        plan.start_rendering()
        assert plan.status == EditPlanStatus.RENDERING

    def test_rendering_to_completed(self):
        plan = self._make_plan(EditPlanStatus.RENDERING)
        plan.mark_completed()
        assert plan.status == EditPlanStatus.COMPLETED

    def test_rendering_to_failed(self):
        plan = self._make_plan(EditPlanStatus.RENDERING)
        plan.mark_failed()
        assert plan.status == EditPlanStatus.FAILED

    def test_completed_to_editing_resume(self):
        plan = self._make_plan(EditPlanStatus.COMPLETED)
        plan.resume_editing()
        assert plan.status == EditPlanStatus.EDITING

    def test_failed_to_editing_resume(self):
        plan = self._make_plan(EditPlanStatus.FAILED)
        plan.resume_editing()
        assert plan.status == EditPlanStatus.EDITING

    def test_failed_to_draft_reset(self):
        plan = self._make_plan(EditPlanStatus.FAILED)
        plan.reset_to_draft()
        assert plan.status == EditPlanStatus.DRAFT

    def test_invalid_start_editing_from_editing(self):
        plan = self._make_plan(EditPlanStatus.EDITING)
        with pytest.raises(ValueError, match="只有 draft 状态"):
            plan.start_editing()

    def test_invalid_start_editing_from_rendering(self):
        plan = self._make_plan(EditPlanStatus.RENDERING)
        with pytest.raises(ValueError):
            plan.start_editing()

    def test_invalid_start_rendering_from_draft(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        with pytest.raises(ValueError, match="只有 editing 状态"):
            plan.start_rendering()

    def test_invalid_start_rendering_from_completed(self):
        plan = self._make_plan(EditPlanStatus.COMPLETED)
        with pytest.raises(ValueError):
            plan.start_rendering()

    def test_invalid_mark_completed_from_draft(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        with pytest.raises(ValueError, match="只有 rendering 状态"):
            plan.mark_completed()

    def test_invalid_mark_failed_from_editing(self):
        plan = self._make_plan(EditPlanStatus.EDITING)
        with pytest.raises(ValueError):
            plan.mark_failed()

    def test_invalid_resume_editing_from_draft(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        with pytest.raises(ValueError, match="只有 completed/failed 状态"):
            plan.resume_editing()

    def test_invalid_resume_editing_from_rendering(self):
        plan = self._make_plan(EditPlanStatus.RENDERING)
        with pytest.raises(ValueError):
            plan.resume_editing()

    def test_invalid_reset_to_draft_from_draft(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        with pytest.raises(ValueError, match="只有 failed 状态"):
            plan.reset_to_draft()

    def test_invalid_reset_to_draft_from_completed(self):
        plan = self._make_plan(EditPlanStatus.COMPLETED)
        with pytest.raises(ValueError):
            plan.reset_to_draft()

    def test_state_transition_updates_updated_at(self):
        plan = self._make_plan(EditPlanStatus.DRAFT)
        old_updated = plan.updated_at
        plan.start_editing()
        assert plan.updated_at >= old_updated


class TestEditPlanDataclass:
    """数据类属性测试."""

    def test_slots_prevents_dynamic_attributes(self):
        plan = EditPlan(id="1", template_id="t1", name="test")
        with pytest.raises(AttributeError):
            plan.new_field = "value"

    def test_full_flow_draft_editing_rendering_completed(self):
        """完整流程：草稿 → 编辑 → 渲染 → 完成."""
        plan = EditPlan.create(template_id="tpl_001", name="完整流程")
        assert plan.status == EditPlanStatus.DRAFT

        plan.start_editing()
        assert plan.status == EditPlanStatus.EDITING

        plan.start_rendering()
        assert plan.status == EditPlanStatus.RENDERING

        plan.mark_completed()
        assert plan.status == EditPlanStatus.COMPLETED

    def test_full_flow_draft_editing_rendering_failed_reset(self):
        """完整流程：草稿 → 编辑 → 渲染 → 失败 → 重置 → 编辑 → 渲染 → 完成."""
        plan = EditPlan.create(template_id="tpl_001", name="失败重试流程")

        plan.start_editing()
        plan.start_rendering()
        plan.mark_failed()
        assert plan.status == EditPlanStatus.FAILED

        plan.reset_to_draft()
        assert plan.status == EditPlanStatus.DRAFT

        plan.start_editing()
        plan.start_rendering()
        plan.mark_completed()
        assert plan.status == EditPlanStatus.COMPLETED
