"""
render_subtitles ASS 字幕纯函数测试.

覆盖 _hex_to_ass_color / _position_to_ass_alignment / _build_ass_style / _escape_ass_text / _format_ass_time 等纯逻辑.
文件生成与 FFmpeg 渲染由集成测试覆盖.
"""

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,
)


class TestHexToAssColor:
    """HEX → ASS 颜色转换（不含 alpha 前缀版本）."""

    def test_white(self):
        assert _hex_to_ass_color("#FFFFFF") == "&HFFFFFF"

    def test_black(self):
        assert _hex_to_ass_color("#000000") == "&H000000"

    def test_red(self):
        # #FF0000 → R=FF, G=00, B=00 → BGR=0000FF
        assert _hex_to_ass_color("#FF0000") == "&H0000FF"

    def test_blue(self):
        # #0000FF → R=00, G=00, B=FF → BGR=FF0000
        assert _hex_to_ass_color("#0000FF") == "&HFF0000"

    def test_green(self):
        # #00FF00 → R=00, G=FF, B=00 → BGR=00FF00
        assert _hex_to_ass_color("#00FF00") == "&H00FF00"

    def test_without_hash(self):
        assert _hex_to_ass_color("FF0000") == "&H0000FF"

    def test_lowercase(self):
        assert _hex_to_ass_color("#ff0000") == "&H0000FF"

    def test_invalid_length_returns_default(self):
        assert _hex_to_ass_color("#FFF") == "&H000000"  # 3位
        assert _hex_to_ass_color("") == "&H000000"  # 空

    def test_mixed_case(self):
        result = _hex_to_ass_color("#aBcDeF")
        assert result == "&HEFCDAB"


class TestPositionToAssAlignment:
    """位置 → ASS 对齐编号映射."""

    def test_top(self):
        assert _position_to_ass_alignment("top") == 8

    def test_center(self):
        assert _position_to_ass_alignment("center") == 5

    def test_bottom(self):
        assert _position_to_ass_alignment("bottom") == 2

    def test_unknown_returns_bottom_default(self):
        assert _position_to_ass_alignment("unknown") == 2
        assert _position_to_ass_alignment("") == 2
        assert _position_to_ass_alignment("left") == 2
        assert _position_to_ass_alignment(None) == 2


class TestBuildAssStyle:
    """构建 ASS Style 行."""

    def test_basic_style(self):
        style = _build_ass_style("Default")
        assert style.startswith("Style: Default,")
        assert "Noto Sans SC" in style
        assert "65" in style  # font_size 48*1.35=65

    def test_custom_font(self):
        style = _build_ass_style("Custom", font_name="Arial", font_size=32)
        assert "Arial" in style
        assert ",43," in style  # font_size 32*1.35=43

    def test_bold(self):
        style = _build_ass_style("Bold", bold=True)
        assert ",-1," in style  # bold = -1 (true)

    def test_not_bold(self):
        style = _build_ass_style("Normal", bold=False)
        parts = style.split(",")
        # Bold 是第 8 个字段（index 7）
        assert parts[7] == "0"

    def test_italic(self):
        style = _build_ass_style("Italic", italic=True)
        parts = style.split(",")
        # Italic 是第 9 个字段（index 8）
        assert parts[8] == "-1"

    def test_alignment(self):
        style = _build_ass_style("Bottom", alignment=2)
        parts = style.split(",")
        # Alignment 是第 19 个字段（index 18）
        assert parts[18] == "2"

    def test_margins(self):
        style = _build_ass_style(
            "Margins",
            margin_v=80,
            margin_l=60,
            margin_r=60,
        )
        parts = style.split(",")
        # MarginL = parts[19], MarginR = parts[20], MarginV = parts[21]
        assert parts[19] == "60"
        assert parts[20] == "60"
        assert parts[21] == "80"

    def test_outline_width(self):
        style = _build_ass_style("Outline", outline_width=3.0)
        parts = style.split(",")
        # Outline 是第 17 个字段（index 16）
        assert parts[16] == "3.0"

    def test_shadow_with_blur(self):
        style = _build_ass_style(
            "Shadow",
            shadow_blur=1.0,
            shadow_offset=(2, 3),
        )
        parts = style.split(",")
        # Shadow 是第 18 个字段（index 17）
        assert parts[17] == "3"  # shadow_offset[1]

    def test_shadow_without_blur(self):
        style = _build_ass_style(
            "NoShadow",
            shadow_blur=0.0,
            shadow_offset=(2, 3),
        )
        parts = style.split(",")
        assert parts[17] == "0"  # 无模糊时阴影深度为0

    def test_style_format_has_correct_field_count(self):
        """ASS Style 行应该有 23 个字段."""
        style = _build_ass_style("Test")
        parts = style.split(",")
        assert len(parts) >= 22  # 至少22个字段（Format定义的）


class TestEscapeAssText:
    """ASS 文本转义."""

    def test_plain_text(self):
        assert _escape_ass_text("hello") == "hello"

    def test_newline_unix(self):
        assert _escape_ass_text("a\nb") == "a\\Nb"

    def test_newline_windows(self):
        assert _escape_ass_text("a\r\nb") == "a\\Nb"

    def test_newline_mac(self):
        assert _escape_ass_text("a\rb") == "a\\Nb"

    def test_curly_braces(self):
        assert _escape_ass_text("{text}") == "(text)"

    def test_multiple_braces(self):
        assert _escape_ass_text("{a}b{c}") == "(a)b(c)"

    def test_mixed_special_chars(self):
        result = _escape_ass_text("line1\n{bold}\nline3")
        assert "\\N" in result
        assert "(bold)" in result
        assert "{" not in result

    def test_empty(self):
        assert _escape_ass_text("") == ""


class TestFormatAssTime:
    """秒 → ASS 时间格式."""

    def test_zero(self):
        assert _format_ass_time(0.0) == "0:00:00.00"

    def test_seconds(self):
        assert _format_ass_time(5.5) == "0:00:05.50"

    def test_minutes(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_exact_minute(self):
        assert _format_ass_time(60.0) == "0:01:00.00"

    def test_exact_hour(self):
        assert _format_ass_time(3600.0) == "1:00:00.00"

    def test_sub_second_precision(self):
        result = _format_ass_time(1.234)
        parts = result.split(":")
        sec_part = parts[2]
        decimals = sec_part.split(".")[1]
        assert len(decimals) == 2  # 两位小数（厘秒）
