"""标题渲染前后端一致性测试。

验证 build_ass_content 生成的 ASS 样式参数与前端 drawTitleOnCanvas.ts 一致：
- 字号不再设置上限，由前端/调用方控制
- 描边宽度 2px
- 阴影 blur=4, offset=2
- boolean stroke/shadow 自动转换
"""

import sys
from pathlib import Path

import pytest

# Ensure packages is importable
sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "packages"))

from domain.ass_subtitle_builder import build_ass_content, build_ass_style


class TestFontSize:
    """字号处理：默认值与保留逻辑，不再做上限截断。"""

    def test_default_font_size_is_36(self):
        """无 size 字段时，默认字号应为 36。"""
        config = {"text": "test"}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        assert ",49," in content, f"默认字号36应补偿为49(36*1.35)，实际内容: {content}"

    def test_size_32_preserved(self):
        """size=32 应原样使用。"""
        config = {"size": 32}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        assert ",43," in content  # 32*1.35=43

    def test_size_60_preserved(self):
        """size=60 应原样保留（字号上限已移除）。"""
        config = {"size": 60}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        font_size = int(fields[2])
        assert font_size == 81, f"字号60应补偿为81(60*1.35), 实际={font_size}"

    def test_font_size_alias_normalized(self):
        """前端传 font_size 应归一化为 size。"""
        config = {"font_size": 52}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        assert fields[2] == "70", f"font_size=52 应补偿为70(52*1.35), 实际={fields[2]}"

    def test_font_color_alias_normalized(self):
        """前端传 font_color 应归一化为 color。"""
        config = {"font_color": "#00FF00"}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        # 绿色 → &H00FF00
        assert "&H00FF00" in content

    def test_size_24_preserved(self):
        """size=24 应原样使用。"""
        config = {"size": 24}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        assert ",32," in content  # 24*1.35=32.4→32


class TestBooleanStrokeNormalization:
    """前端 stroke=true/false 应自动转换为标准 dict。"""

    def test_stroke_true_enables_outline(self):
        """stroke=true 应生成 outline_width=2 的样式。"""
        config = {"stroke": True}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        # 解析 Style 行的 Outline 字段（第17个字段，索引16）
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        outline_width = float(fields[16])
        assert outline_width == 2.0, f"stroke=true 应产生 outline_width=2, 实际={outline_width}"

    def test_stroke_false_no_outline(self):
        """stroke=false 应生成 outline_width=0 的样式。"""
        config = {"stroke": False}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        outline_width = float(fields[16])
        assert outline_width == 0.0, f"stroke=false 应产生 outline_width=0, 实际={outline_width}"

    def test_stroke_dict_still_works(self):
        """stroke={enabled:true, width:3} 仍应正常工作。"""
        config = {"stroke": {"enabled": True, "width": 3, "color": "#FF0000"}}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        outline_width = float(fields[16])
        assert outline_width == 3.0, f"自定义stroke width=3 应保留, 实际={outline_width}"


class TestBooleanShadowNormalization:
    """前端 shadow=true/false 应自动转换为标准 dict。"""

    def test_shadow_true_enables_shadow(self):
        """shadow=true 应生成 shadow_depth=2 的样式。"""
        config = {"shadow": True}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        # Shadow 字段是第18个（索引17）
        shadow_depth = int(fields[17])
        assert shadow_depth == 2, f"shadow=true 应产生 shadow_depth=2, 实际={shadow_depth}"

    def test_shadow_false_no_shadow(self):
        """shadow=false 应生成 shadow_depth=0 的样式。"""
        config = {"shadow": False}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        shadow_depth = int(fields[17])
        assert shadow_depth == 0, f"shadow=false 应产生 shadow_depth=0, 实际={shadow_depth}"

    def test_shadow_dict_still_works(self):
        """shadow={enabled:true, blur:8} 仍应正常工作。"""
        config = {"shadow": {"enabled": True, "blur": 8, "offset_x": 3, "offset_y": 3}}
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="测试标题",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]
        shadow_depth = int(fields[17])
        assert shadow_depth == 3, f"自定义shadow offset_y=3 应保留, 实际={shadow_depth}"


class TestFullStyleConsistency:
    """完整样式参数一致性测试。"""

    def test_frontend_default_style_matches_backend(self):
        """前端默认样式参数应在后端产生一致的 ASS 输出。

        前端默认：font_size=24(或用户设置), bold=false, stroke=true, shadow=true, color=#FFFFFF
        """
        config = {
            "text": "标题文本",
            "font": "思源黑体",
            "size": 28,
            "color": "#FFFFFF",
            "bold": True,
            "italic": False,
            "stroke": True,
            "shadow": True,
            "position": "top",
        }
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="标题文本",
            title_config=config,
        )
        style_line = [line for line in content.splitlines() if line.startswith("Style: TitleStyle")][0]
        fields = [f.strip() for f in style_line.split(",")]

        # Fontname
        assert fields[1] == "Noto Sans SC"
        # Fontsize = 28*1.35=37.8→38
        assert fields[2] == "38"
        # Bold = -1 (True)
        assert fields[7] == "-1"
        # Outline width = 2 (前端默认 stroke width)
        assert float(fields[16]) == 2.0
        # Shadow depth = 2 (offset_y)
        assert int(fields[17]) == 2
        # Alignment = 8 (top)
        assert int(fields[18]) == 8


class TestTitleSlashNewline:
    """用户输入 / 或 ／ 应触发标题换行。"""

    def test_halfwidth_slash_in_title(self):
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="第一行/第二行",
            title_config={"size": 48},
        )
        for line in content.splitlines():
            if "Dialogue" in line and "TitleStyle" in line:
                assert "\\N" in line, f"斜杠应转为换行: {line}"
                assert "第一行" in line
                assert "第二行" in line
                break
        else:
            pytest.fail("未找到 TitleStyle Dialogue 行")

    def test_fullwidth_slash_in_title(self):
        content = build_ass_content(
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="第一行／第二行",
            title_config={"size": 48},
        )
        for line in content.splitlines():
            if "Dialogue" in line and "TitleStyle" in line:
                assert "\\N" in line, f"全角斜杠应转为换行: {line}"
                break
        else:
            pytest.fail("未找到 TitleStyle Dialogue 行")
