"""字幕渲染纯函数测试 — _build_ass_style + generate_ass_subtitles."""

from __future__ import annotations

from pathlib import Path

import pytest
from video_processing.render_subtitles import (
    _build_ass_style,
    _escape_ass_text,
    _format_ass_time,
    _hex_to_ass_color,
    _position_to_ass_alignment,
    generate_ass_subtitles,
)


class TestHexToAssColor:
    """_hex_to_ass_color 颜色转换测试."""

    def test_white(self):
        """白色 #FFFFFF → &HFFFFFF (ASS BGR格式)."""
        assert _hex_to_ass_color("#FFFFFF") == "&HFFFFFF"

    def test_black(self):
        """黑色 #000000 → &H000000."""
        assert _hex_to_ass_color("#000000") == "&H000000"

    def test_red(self):
        """红色 #FF0000 → ASS是BGR顺序 → &H0000FF."""
        assert _hex_to_ass_color("#FF0000") == "&H0000FF"

    def test_blue(self):
        """蓝色 #0000FF → BGR → &HFF0000."""
        assert _hex_to_ass_color("#0000FF") == "&HFF0000"

    def test_green(self):
        """绿色 #00FF00 → BGR → &H00FF00."""
        assert _hex_to_ass_color("#00FF00") == "&H00FF00"

    def test_lowercase(self):
        """小写hex也支持."""
        assert _hex_to_ass_color("#ff0000") == "&H0000FF"

    def test_no_hash_prefix(self):
        """不带#的颜色."""
        assert _hex_to_ass_color("FF0000") == "&H0000FF"

    def test_invalid_length_fallback(self):
        """长度不对返回默认黑色."""
        assert _hex_to_ass_color("#FFF") == "&H000000"


class TestPositionToAssAlignment:
    """_position_to_ass_alignment 位置映射测试."""

    def test_top_center(self):
        """top → 上中(8)."""
        assert _position_to_ass_alignment("top") == 8

    def test_bottom_center(self):
        """bottom → 下中(2)."""
        assert _position_to_ass_alignment("bottom") == 2

    def test_center_middle(self):
        """center → 居中(5)."""
        assert _position_to_ass_alignment("center") == 5

    def test_unknown_defaults_to_bottom(self):
        """未知位置默认底部(2)."""
        assert _position_to_ass_alignment("unknown") == 2
        assert _position_to_ass_alignment("top_left") == 2
        assert _position_to_ass_alignment("bottom_right") == 2

    def test_empty_string_defaults_to_bottom(self):
        """空字符串默认底部."""
        assert _position_to_ass_alignment("") == 2


class TestBuildAssStyle:
    """_build_ass_style ASS样式行构建测试."""

    def test_basic_style_line(self):
        """基本样式行包含关键字段."""
        line = _build_ass_style("Default")
        assert line.startswith("Style: Default,")
        assert "Noto Sans SC" in line
        assert "65" in line  # font_size 48*1.35=65

    def test_bold_enabled(self):
        """加粗时Bold=-1."""
        line = _build_ass_style("Bold", bold=True)
        assert "Style: Bold," in line
        # Bold字段位置：第7个逗号分隔字段=Bold=-1
        parts = line.split(",")
        # Name, Fontname, Fontsize, Primary, Secondary, Outline, Back, Bold, ...
        assert parts[7] == "-1"  # Bold

    def test_bold_disabled(self):
        """不加粗时Bold=0."""
        line = _build_ass_style("Normal", bold=False)
        parts = line.split(",")
        assert parts[7] == "0"

    def test_italic_enabled(self):
        """斜体时Italic=-1."""
        line = _build_ass_style("Italic", italic=True)
        parts = line.split(",")
        assert parts[8] == "-1"  # Italic

    def test_custom_font_size(self):
        """自定义字号."""
        line = _build_ass_style("Big", font_size=72)
        parts = line.split(",")
        assert parts[2] == "97"  # Fontsize 72*1.35=97

    def test_custom_alignment(self):
        """自定义对齐方式."""
        line = _build_ass_style("Bottom", alignment=2)
        # Alignment是第16个字段（数一下）
        # Name, Fontname, Fontsize, Primary, Secondary, Outline, Back, Bold, Italic, Underline, Strikeout, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, ...
        parts = line.split(",")
        assert parts[18] == "2"  # Alignment (0-indexed: 18)

    def test_outline_width(self):
        """描边宽度."""
        line = _build_ass_style("Outline", outline_width=3.0)
        parts = line.split(",")
        assert parts[16] == "3.0"  # Outline (BorderStyle后是Outline)


class TestEscapeAssText:
    """_escape_ass_text 文本转义测试."""

    def test_plain_text_unchanged(self):
        """普通文本不变."""
        assert _escape_ass_text("hello world") == "hello world"

    def test_newline_converted(self):
        """换行符转成\\N."""
        assert _escape_ass_text("line1\nline2") == "line1\\Nline2"

    def test_crlf_converted(self):
        """CRLF转成\\N."""
        assert _escape_ass_text("a\r\nb") == "a\\Nb"

    def test_carriage_return_converted(self):
        """纯\\r转成\\N."""
        assert _escape_ass_text("a\rb") == "a\\Nb"

    def test_curly_braces_replaced(self):
        """大括号转成圆括号（防止ASS样式注入）."""
        assert _escape_ass_text("{text}") == "(text)"

    def test_mixed_special_chars(self):
        """混合特殊字符."""
        result = _escape_ass_text("line1\nline2 {bold}\rlast")
        assert "line1\\Nline2 (bold)\\Nlast" == result


class TestFormatAssTime:
    """_format_ass_time 时间格式化测试."""

    def test_zero_seconds(self):
        """0秒."""
        assert _format_ass_time(0) == "0:00:00.00"

    def test_seconds_only(self):
        """只有秒."""
        assert _format_ass_time(5.5) == "0:00:05.50"

    def test_minutes_and_seconds(self):
        """几分几秒."""
        assert _format_ass_time(65.25) == "0:01:05.25"

    def test_hours(self):
        """几小时."""
        assert _format_ass_time(3661.5) == "1:01:01.50"

    def test_always_two_decimal_places(self):
        """总是两位小数."""
        assert _format_ass_time(1.0) == "0:00:01.00"
        assert _format_ass_time(1.1) == "0:00:01.10"


class TestGenerateAssSubtitles:
    """generate_ass_subtitles ASS字幕文件生成测试."""

    def test_no_subtitles_empty_file(self, tmp_path):
        """没有字幕生成空文件."""
        output = tmp_path / "empty.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
        )
        assert result == output
        assert output.exists()
        assert output.read_text(encoding="utf-8") == ""

    def test_title_only(self, tmp_path):
        """只有标题."""
        output = tmp_path / "title.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=30.0,
            title_text="测试标题",
        )
        assert result == output
        content = output.read_text(encoding="utf-8")
        assert "Script Info" in content
        assert "PlayResX: 1080" in content
        assert "PlayResY: 1920" in content
        assert "测试标题" in content
        assert "V4+ Styles" in content
        assert "Events" in content

    def test_subtitle_only(self, tmp_path):
        """只有底部字幕."""
        output = tmp_path / "sub.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=15.0,
            subtitle_text="这是字幕",
        )
        assert result == output
        content = output.read_text(encoding="utf-8")
        assert "这是字幕" in content
        assert "PlayResX: 1080" in content

    def test_both_title_and_subtitle(self, tmp_path):
        """标题+字幕都有."""
        output = tmp_path / "both.ass"
        result = generate_ass_subtitles(
            output,
            video_width=720,
            video_height=1280,
            video_duration=20.0,
            title_text="大标题",
            subtitle_text="底部字幕",
        )
        content = output.read_text(encoding="utf-8")
        assert "大标题" in content
        assert "底部字幕" in content
        # 应该有两种样式（title和subtitle）
        assert content.count("Style:") >= 2

    def test_title_disabled_by_config(self, tmp_path):
        """通过config禁用标题."""
        output = tmp_path / "disabled_title.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="标题",
            title_config={"enabled": False},
            subtitle_text="字幕",
        )
        content = output.read_text(encoding="utf-8")
        assert "标题" not in content
        assert "字幕" in content

    def test_empty_title_text_not_rendered(self, tmp_path):
        """空标题文本不渲染."""
        output = tmp_path / "empty_title.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="   ",
            subtitle_text="有字幕",
        )
        content = output.read_text(encoding="utf-8")
        assert "有字幕" in content

    def test_title_color_overlay(self, tmp_path):
        """自定义标题颜色."""
        output = tmp_path / "color.ass"
        result = generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=10.0,
            title_text="红色标题",
            title_config={"color": "#FF0000"},
        )
        content = output.read_text(encoding="utf-8")
        # 红色 → ASS BGR格式 &H0000FF
        assert "&H0000FF" in content

    def test_dialogue_line_format(self, tmp_path):
        """Dialogue行格式正确."""
        output = tmp_path / "dialogue.ass"
        generate_ass_subtitles(
            output,
            video_width=1080,
            video_height=1920,
            video_duration=5.0,
            subtitle_text="测试字幕文本",
        )
        content = output.read_text(encoding="utf-8")
        assert "Dialogue:" in content
        assert "测试字幕文本" in content
