"""chroma_key_config 领域模型单测."""

from __future__ import annotations

import pytest

from packages.domain.chroma_key_config import (
    CHROMA_KEY_PRESETS,
    ChromaKeyConfig,
    apply_chroma_key_if_needed,
    build_chromakey_filter,
    build_colorkey_filter,
    get_preset_names,
    normalize_color,
)

# ── ChromaKeyConfig.from_dict 测试 ────────────────────────────────────────


class TestChromaKeyConfigFromDict:
    def test_none_returns_disabled(self):
        cfg = ChromaKeyConfig.from_dict(None)
        assert cfg.enabled is False

    def test_empty_dict_returns_disabled(self):
        cfg = ChromaKeyConfig.from_dict({})
        assert cfg.enabled is False

    def test_disabled_returns_disabled(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": False})
        assert cfg.enabled is False

    def test_enabled_default_params(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True})
        assert cfg.enabled is True
        assert cfg.key_color == "#00FF00"
        assert cfg.similarity == 0.3
        assert cfg.blend == 0.1
        assert cfg.spill_suppress == 0.0

    def test_custom_params(self):
        cfg = ChromaKeyConfig.from_dict(
            {
                "enabled": True,
                "key_color": "#0000FF",
                "similarity": 0.5,
                "blend": 0.2,
                "spill_suppress": 0.4,
            }
        )
        assert cfg.key_color == "#0000FF"
        assert cfg.similarity == 0.5
        assert cfg.blend == 0.2
        assert cfg.spill_suppress == 0.4

    def test_similarity_clamped_low(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": 0.001})
        assert cfg.similarity == 0.01

    def test_similarity_clamped_high(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": 2.0})
        assert cfg.similarity == 1.0

    def test_blend_clamped_low(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "blend": -0.5})
        assert cfg.blend == 0.0

    def test_blend_clamped_high(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "blend": 1.5})
        assert cfg.blend == 1.0

    def test_spill_suppress_clamped(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "spill_suppress": 2.0})
        assert cfg.spill_suppress == 1.0

    def test_invalid_similarity_type_uses_default(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "similarity": "high"})
        assert cfg.similarity == 0.3

    def test_key_color_stripped(self):
        cfg = ChromaKeyConfig.from_dict({"enabled": True, "key_color": "  #00FF00  "})
        assert cfg.key_color == "#00FF00"


# ── from_preset 测试 ──────────────────────────────────────────────────────


class TestFromPreset:
    def test_green_screen_preset(self):
        cfg = ChromaKeyConfig.from_preset("green_screen")
        assert cfg is not None
        assert cfg.enabled is True
        assert cfg.key_color == "#00FF00"
        assert cfg.similarity == 0.3

    def test_blue_screen_preset(self):
        cfg = ChromaKeyConfig.from_preset("blue_screen")
        assert cfg is not None
        assert cfg.key_color == "#0000FF"

    def test_invalid_preset_returns_none(self):
        assert ChromaKeyConfig.from_preset("nonexistent") is None

    def test_all_presets_valid(self):
        for name in CHROMA_KEY_PRESETS:
            cfg = ChromaKeyConfig.from_preset(name)
            assert cfg is not None
            assert cfg.enabled is True


# ── has_effect / validate 测试 ────────────────────────────────────────────


class TestHasEffectAndValidate:
    def test_disabled_no_effect(self):
        cfg = ChromaKeyConfig(enabled=False)
        assert cfg.has_effect() is False

    def test_enabled_has_effect(self):
        cfg = ChromaKeyConfig(enabled=True, similarity=0.3)
        assert cfg.has_effect() is True

    def test_zero_similarity_no_effect(self):
        cfg = ChromaKeyConfig(enabled=True, similarity=0.0)
        # similarity 被钳制后为 0.01，所以应该有效果
        # 等等，from_dict 才会钳制，直接构造不会
        assert cfg.has_effect() is False

    def test_validate_disabled_valid(self):
        cfg = ChromaKeyConfig(enabled=False)
        ok, msg = cfg.validate()
        assert ok is True
        assert msg == ""

    def test_validate_enabled_valid(self):
        cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00")
        ok, msg = cfg.validate()
        assert ok is True

    def test_validate_empty_color_invalid(self):
        cfg = ChromaKeyConfig(enabled=True, key_color="")
        ok, msg = cfg.validate()
        assert ok is False
        assert "key_color" in msg

    def test_validate_similarity_out_of_range(self):
        cfg = ChromaKeyConfig(enabled=True, similarity=2.0)
        ok, msg = cfg.validate()
        assert ok is False
        assert "similarity" in msg


# ── normalize_color 测试 ──────────────────────────────────────────────────


class TestNormalizeColor:
    def test_hex_with_hash(self):
        assert normalize_color("#00FF00") == "0x00FF00"

    def test_hex_lowercase(self):
        assert normalize_color("#00ff00") == "0x00FF00"

    def test_hex_without_hash(self):
        assert normalize_color("00FF00") == "0x00FF00"

    def test_hex_with_alpha(self):
        assert normalize_color("#00FF00FF") == "0x00FF00"

    def test_already_0x_format(self):
        assert normalize_color("0x00FF00") == "0X00FF00"

    def test_0x_lowercase(self):
        assert normalize_color("0x00ff00") == "0X00FF00"

    def test_color_name_passthrough(self):
        assert normalize_color("green") == "green"
        assert normalize_color("blue") == "blue"

    def test_whitespace_stripped(self):
        assert normalize_color("  #FF0000  ") == "0xFF0000"


# ── build_colorkey_filter 测试 ────────────────────────────────────────────


class TestBuildColorkeyFilter:
    def test_disabled_returns_copy(self):
        cfg = ChromaKeyConfig(enabled=False)
        result = build_colorkey_filter(cfg, "[in]", "[out]")
        assert "copy" in result
        assert "[in]" in result
        assert "[out]" in result

    def test_basic_colorkey(self):
        cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1)
        result = build_colorkey_filter(cfg, "[v]", "[ck]")
        assert "colorkey=" in result
        assert "color=0x00FF00" in result
        assert "similarity=0.3" in result
        assert "blend=0.1" in result
        assert "[v]" in result
        assert "[ck]" in result

    def test_with_spill_suppress(self):
        cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", spill_suppress=0.5)
        result = build_colorkey_filter(cfg, "[in]", "[out]")
        assert "colorchannelmixer=" in result
        assert "rr=" in result
        assert "gg=" in result
        assert "bb=" in result

    def test_no_spill_suppress_no_colorchannelmixer(self):
        cfg = ChromaKeyConfig(enabled=True, spill_suppress=0.0)
        result = build_colorkey_filter(cfg, "[in]", "[out]")
        assert "colorchannelmixer" not in result


# ── build_chromakey_filter 测试 ───────────────────────────────────────────


class TestBuildChromakeyFilter:
    def test_disabled_returns_copy(self):
        cfg = ChromaKeyConfig(enabled=False)
        result = build_chromakey_filter(cfg, "[in]", "[out]")
        assert "copy" in result

    def test_basic_chromakey(self):
        cfg = ChromaKeyConfig(enabled=True, key_color="#00FF00", similarity=0.3, blend=0.1)
        result = build_chromakey_filter(cfg, "[v]", "[ck]")
        assert "chromakey=" in result
        assert "color=0x00FF00" in result
        assert "similarity=0.3" in result
        assert "blend=0.1" in result

    def test_contains_input_and_output_labels(self):
        cfg = ChromaKeyConfig(enabled=True)
        result = build_chromakey_filter(cfg, "[in_v]", "[out_v]")
        assert "[in_v]" in result
        assert "[out_v]" in result


# ── apply_chroma_key_if_needed 测试 ───────────────────────────────────────


class TestApplyChromaKeyIfNeeded:
    def test_none_config_returns_none(self):
        assert apply_chroma_key_if_needed(None, "[in]", "[out]") is None

    def test_no_chroma_key_returns_none(self):
        assert apply_chroma_key_if_needed({}, "[in]", "[out]") is None

    def test_disabled_chroma_key_returns_none(self):
        config = {"chroma_key": {"enabled": False}}
        assert apply_chroma_key_if_needed(config, "[in]", "[out]") is None

    def test_enabled_chroma_key_returns_filter(self):
        config = {"chroma_key": {"enabled": True, "key_color": "#00FF00"}}
        result = apply_chroma_key_if_needed(config, "[in]", "[out]")
        assert result is not None
        assert "colorkey" in result

    def test_invalid_config_handles_exception(self):
        # 传入无效配置触发异常，应该返回 None 而不是抛出
        config = {"chroma_key": "invalid_string"}
        result = apply_chroma_key_if_needed(config, "[in]", "[out]")
        assert result is None


# ── 预设工具函数测试 ───────────────────────────────────────────────────────


class TestPresetUtils:
    def test_get_preset_names_returns_sorted_list(self):
        names = get_preset_names()
        assert isinstance(names, list)
        assert len(names) == len(CHROMA_KEY_PRESETS)
        assert names == sorted(names)

    def test_all_preset_names_in_presets_dict(self):
        for name in get_preset_names():
            assert name in CHROMA_KEY_PRESETS
