"""Tests for EditPlanService.replace_all_clips_transactional."""

from __future__ import annotations

import os
from unittest.mock import MagicMock, PropertyMock, patch

import pytest

os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")


class TestReplaceAllClipsTransactional:
    """事务性替换片段方法测试。"""

    @patch("packages.adapters.sqlalchemy_impl.models.EditPlanClipModel")
    @patch("app.services.edit_plan_service.EditPlanClip")
    def test_success_commits_once(self, mock_clip_cls, mock_model_cls):
        """成功时单次 commit，不 rollback。"""
        from app.services.edit_plan_service import EditPlanService

        db = MagicMock()
        # Mock query chain for delete
        query_mock = MagicMock()
        query_mock.filter.return_value.delete.return_value = 3
        db.query.return_value = query_mock

        # Mock query chain for mark_ready (pending_with_asset)
        # After the create loop, query returns empty list (no pending clips with asset)
        ready_query = MagicMock()
        ready_query.filter.return_value.filter.return_value.filter.return_value.all.return_value = []
        db.query.side_effect = [query_mock, ready_query]

        # Mock EditPlanClip.create to return a mock entity
        mock_entity = MagicMock()
        mock_entity.id = "clip-1"
        mock_entity.plan_id = "plan-1"
        mock_entity.clip_type = "main"
        mock_entity.order = 0
        mock_entity.asset_id = "asset-1"
        mock_entity.text_content = ""
        mock_entity.start_time = 0.0
        mock_entity.duration = 3.0
        mock_entity.transition_effect = "cut"
        mock_entity.transition_duration = 0.0
        mock_entity.playback_speed = 1.0
        mock_entity.status.value = "pending"
        mock_entity.config = {}
        mock_clip_cls.create.return_value = mock_entity

        # Mock the model constructor
        mock_model_instance = MagicMock()
        mock_model_cls.return_value = mock_model_instance

        # Mock clip_repo
        clip_repo = MagicMock()
        clip_repo.session = db

        svc = EditPlanService.__new__(EditPlanService)
        svc._clip_repo = clip_repo

        result = svc.replace_all_clips_transactional(
            "plan-1",
            [{"asset_id": "asset-1", "start_time": 0.0, "duration": 3.0, "order": 0}],
        )

        assert result == 1
        db.commit.assert_called_once()
        db.rollback.assert_not_called()
        db.add.assert_called_once_with(mock_model_instance)

    @patch("packages.adapters.sqlalchemy_impl.models.EditPlanClipModel")
    @patch("app.services.edit_plan_service.EditPlanClip")
    def test_failure_rolls_back(self, mock_clip_cls, mock_model_cls):
        """异常时自动 rollback。"""
        from app.services.edit_plan_service import EditPlanService

        db = MagicMock()
        query_mock = MagicMock()
        query_mock.filter.return_value.delete.return_value = 0
        db.query.return_value = query_mock

        # Simulate failure during create
        mock_clip_cls.create.side_effect = ValueError("模拟异常")

        clip_repo = MagicMock()
        clip_repo.session = db

        svc = EditPlanService.__new__(EditPlanService)
        svc._clip_repo = clip_repo

        with pytest.raises(ValueError, match="模拟异常"):
            svc.replace_all_clips_transactional(
                "plan-1",
                [{"asset_id": "bad", "start_time": 0.0, "duration": 1.0, "order": 0}],
            )

        db.rollback.assert_called_once()
        db.commit.assert_not_called()

    @patch("packages.adapters.sqlalchemy_impl.models.EditPlanClipModel")
    @patch("app.services.edit_plan_service.EditPlanClip")
    def test_order_defaults_to_index(self, mock_clip_cls, mock_model_cls):
        """order=0 时使用索引值作为 order。"""
        from app.services.edit_plan_service import EditPlanService

        db = MagicMock()
        query_mock = MagicMock()
        query_mock.filter.return_value.delete.return_value = 0
        db.query.return_value = query_mock

        ready_query = MagicMock()
        ready_query.filter.return_value.filter.return_value.filter.return_value.all.return_value = []
        db.query.side_effect = [query_mock, ready_query]

        mock_entity = MagicMock()
        mock_entity.id = "clip-1"
        mock_entity.plan_id = "plan-1"
        mock_entity.clip_type = "main"
        mock_entity.order = 0  # order=0 → 使用 i=0
        mock_entity.asset_id = "a1"
        mock_entity.text_content = ""
        mock_entity.start_time = 0.0
        mock_entity.duration = 1.0
        mock_entity.transition_effect = "cut"
        mock_entity.transition_duration = 0.0
        mock_entity.playback_speed = 1.0
        mock_entity.status.value = "pending"
        mock_entity.config = {}
        mock_clip_cls.create.return_value = mock_entity

        mock_model_cls.return_value = MagicMock()

        clip_repo = MagicMock()
        clip_repo.session = db

        svc = EditPlanService.__new__(EditPlanService)
        svc._clip_repo = clip_repo

        svc.replace_all_clips_transactional(
            "plan-1",
            [{"asset_id": "a1", "start_time": 0.0, "duration": 1.0, "order": 0}],
        )

        # order=0 → falsy → use index i=0
        create_call = mock_clip_cls.create.call_args
        assert create_call.kwargs["order"] == 0
