"""_get_template_segments 回退路径测试.

验证三级回退链：
1. 新模板系统（tpl_svc.list_clip_configs）正常 → 直接返回
2. 新模板系统主表不存在（ValueError）→ 直接查 template_clip_configs 表兜底
3. 直接查表也失败 → 回退旧模板系统（template_segments）
4. 全部失败 → 返回空列表

覆盖 P0 修复：自建模板在 edit_templates 主表不存在但在 template_clip_configs 有记录时，
from-assets 流程不再 400。
"""

from __future__ import annotations

import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, PropertyMock

os.environ.setdefault("JWT_SECRET_KEY", "unit-test-secret-key-for-testing")
os.environ.setdefault("DATABASE_URL", "sqlite:///test.db")

sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "apps" / "api"))

from app.api.routes.templates_editor.clips import _get_template_segments

TEST_TEMPLATE_ID = "tmpl-orphan-001"
DEFAULT_DUR = 5.0  # _DEFAULT_EDITOR_CLIP_DURATION


def _make_clip_config(order: int, min_dur: float = 3.0, max_dur: float = 8.0):
    """构造 mock TemplateClipConfig 领域实体."""
    cc = MagicMock()
    cc.order = order
    cc.min_duration = min_dur
    cc.max_duration = max_dur
    return cc


def _make_old_segment(segment_order: int, dur_min: float = 4.0, dur_max: float = 7.0):
    """构造 mock 旧 TemplateSegment."""
    s = MagicMock()
    s.segment_order = segment_order
    s.duration_min = dur_min
    s.duration_max = dur_max
    return s


# ---------------------------------------------------------------------------
# 测试
# ---------------------------------------------------------------------------


class TestGetTemplateSegmentsFallback:
    """_get_template_segments 三级回退链."""

    def test_new_system_works(self):
        """路径1：新模板系统正常返回 → 直接使用."""
        configs = [_make_clip_config(0, 2.0, 6.0), _make_clip_config(1, 3.0, 9.0)]
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.return_value = configs
        db = MagicMock()

        result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert len(result) == 2
        assert result[0] == (0, 2.0, 6.0)
        assert result[1] == (1, 3.0, 9.0)
        tpl_svc.list_clip_configs.assert_called_once_with(TEST_TEMPLATE_ID)

    def test_main_table_missing_direct_query_succeeds(self):
        """路径2（P0修复）：主表不存在 ValueError → 直接查表成功.

        模拟自建模板在 edit_templates 主表已删除/不存在，
        但 template_clip_configs 表有记录。
        """
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.side_effect = ValueError(f"模板不存在: {TEST_TEMPLATE_ID}")
        db = MagicMock()

        # Mock SQLAlchemyTemplateClipConfigRepository
        direct_configs = [
            _make_clip_config(0, 2.0, 5.0),
            _make_clip_config(1, 3.0, 7.0),
            _make_clip_config(2, 4.0, 8.0),
        ]
        with (
            __import__("unittest.mock", fromlist=["patch"]).patch(
                "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
            ) as mock_repo_cls,
        ):
            mock_repo = MagicMock()
            mock_repo.list_by_template.return_value = direct_configs
            mock_repo_cls.return_value = mock_repo

            result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert len(result) == 3
        assert result[0] == (0, 2.0, 5.0)
        assert result[1] == (1, 3.0, 7.0)
        assert result[2] == (2, 4.0, 8.0)
        mock_repo.list_by_template.assert_called_once_with(TEST_TEMPLATE_ID)

    def test_main_table_missing_direct_query_empty_falls_to_old(self):
        """路径2→3：主表不存在 + 直接查表为空 → 回退旧系统."""
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.side_effect = ValueError("模板不存在")
        db = MagicMock()

        old_segments = [_make_old_segment(0, 3.0, 6.0)]

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            mock_repo = MagicMock()
            mock_repo.list_by_template.return_value = []  # 新表也没记录
            mock_repo_cls.return_value = mock_repo

            with __import__("unittest.mock", fromlist=["patch"]).patch(
                "app.api.routes.templates_editor.clips.SQLAlchemyTemplateRepository"
            ) as mock_old_cls:
                mock_old = MagicMock()
                mock_old.list_segments.return_value = old_segments
                mock_old_cls.return_value = mock_old

                result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert len(result) == 1
        assert result[0] == (0, 3.0, 6.0)

    def test_all_fail_returns_empty(self):
        """路径4：三级全部失败 → 返回空列表."""
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.side_effect = ValueError("模板不存在")
        db = MagicMock()

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            mock_repo = MagicMock()
            mock_repo.list_by_template.side_effect = Exception("DB error")
            mock_repo_cls.return_value = mock_repo

            with __import__("unittest.mock", fromlist=["patch"]).patch(
                "app.api.routes.templates_editor.clips.SQLAlchemyTemplateRepository"
            ) as mock_old_cls:
                mock_old = MagicMock()
                mock_old.list_segments.return_value = []  # 旧表也空
                mock_old_cls.return_value = mock_old

                result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert result == []

    def test_direct_query_sorts_by_order(self):
        """直接查表返回的结果按 order 排序."""
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.side_effect = ValueError("模板不存在")
        db = MagicMock()

        # 故意乱序
        configs = [
            _make_clip_config(2, 5.0, 10.0),
            _make_clip_config(0, 2.0, 4.0),
            _make_clip_config(1, 3.0, 6.0),
        ]

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            mock_repo = MagicMock()
            mock_repo.list_by_template.return_value = configs
            mock_repo_cls.return_value = mock_repo

            result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert [r[0] for r in result] == [0, 1, 2]
        assert result[0] == (0, 2.0, 4.0)
        assert result[1] == (1, 3.0, 6.0)
        assert result[2] == (2, 5.0, 10.0)

    def test_direct_query_handles_none_durations(self):
        """直接查表时 min/max_duration 为 None → 使用默认值."""
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.side_effect = ValueError("模板不存在")
        db = MagicMock()

        cc = MagicMock()
        cc.order = 0
        cc.min_duration = None
        cc.max_duration = None

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            mock_repo = MagicMock()
            mock_repo.list_by_template.return_value = [cc]
            mock_repo_cls.return_value = mock_repo

            result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        assert len(result) == 1
        # None → default (5.0), max(None or None) → default (5.0)
        assert result[0] == (0, DEFAULT_DUR, DEFAULT_DUR)

    def test_new_system_returns_empty_tries_direct(self):
        """新模板系统返回空列表（非异常）→ 继续尝试直接查表."""
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.return_value = []  # 空列表，非异常
        db = MagicMock()

        direct_configs = [_make_clip_config(0, 3.0, 6.0)]

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            mock_repo = MagicMock()
            mock_repo.list_by_template.return_value = direct_configs
            mock_repo_cls.return_value = mock_repo

            result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)

        # 新系统返回空 → 不走 except → 但也没 return → 继续往下走
        # 直接查表有数据 → 返回
        assert len(result) == 1
        assert result[0] == (0, 3.0, 6.0)

    def test_existing_template_unaffected(self):
        """正常模板（主表存在）行为不变."""
        configs = [_make_clip_config(0, 2.0, 5.0)]
        tpl_svc = MagicMock()
        tpl_svc.list_clip_configs.return_value = configs
        db = MagicMock()

        with __import__("unittest.mock", fromlist=["patch"]).patch(
            "app.api.routes.templates_editor.clips.SQLAlchemyTemplateClipConfigRepository"
        ) as mock_repo_cls:
            result = _get_template_segments(TEST_TEMPLATE_ID, tpl_svc, db)
            # 直接查表不应被调用（新系统已返回）
            mock_repo_cls.assert_not_called()

        assert len(result) == 1
        assert result[0] == (0, 2.0, 5.0)
