"""Unit tests for the config-actor combined format helpers (issue #11189).

Tests that ``_detect_nested_config_actor``, ``_flatten_config_actor``, and
``is_v3_yaml`` correctly recognise both the compact string form of the
combined-format actor and the nested-dict form.
"""

from __future__ import annotations

import pytest

from cleveragents.actor.schema import (
    _detect_nested_config_actor,
    _flatten_config_actor,
    is_v3_yaml,
)


class TestDetectNestedConfigActor:
    """Tests for ``_detect_nested_config_actor``."""

    def test_compact_string_format(self):
        blob = {"name": "local/test", "config": {"actor": "aws/gpt-4"}}
        assert _detect_nested_config_actor(blob) is True

    def test_empty_string_does_not_count(self):
        """An actor with an empty string value is not a combined format signal."""
        blob = {"name": "local/test", "config": {"actor": ""}}
        assert _detect_nested_config_actor(blob) is False

    def test_nested_dict_with_type(self):
        blob = {
            "name": "local/test",
            "config": {"actor": {"type": "llm", "provider": "gcp", "model": "gemini"}},
        }
        assert _detect_nested_config_actor(blob) is True

    def test_nested_dict_with_null_type_still_counts(self):
        """A nested dict with type=null triggers detection (let downstream validate)."""
        blob = {
            "name": "local/test",
            "config": {"actor": {"type": None, "provider": "gcp"}},
        }
        assert _detect_nested_config_actor(blob) is True

    def test_no_config_key(self):
        blob = {"name": "local/test", "type": "llm"}
        assert _detect_nested_config_actor(blob) is False

    def test_none_type_field_inside_nested_actor(self):
        """Actor with null type inside is still detected because non-null presence."""
        blob = {
            "config": {"actor": {"provider": "azure"}},  # no 'type' key at all
        }
        assert _detect_nested_config_actor(blob) is False

    def test_nested_dict_with_version_falls_back(self):
        """Without type, version field inside config causes fallback detection."""
        blob = {
            "config": {"actor": {}, "version": "3"},
        }
        assert _detect_nested_config_actor(blob) is True


class TestFlattenConfigActor:
    """Tests for ``_flatten_config_actor``."""

    def test_flattens_compact_string(self):
        blob = {"name": "local/test", "config": {"actor": "aws/gpt-4"}}
        flat = _flatten_config_actor(blob)
        assert "config" not in flat
        assert flat["provider"] == "aws"
        assert flat["model"] == "gpt-4"
        assert flat["name"] == "local/test"

    def test_flattens_nested_dict(self):
        blob = {
            "config": {"actor": {"type": "llm", "provider": "gcp", "model": "gemini"}},
            "name": "local/nested",
        }
        flat = _flatten_config_actor(blob)
        assert "config" not in flat
        assert flat["type"] == "llm"
        assert flat["provider"] == "gcp"
        assert flat["model"] == "gemini"

    def test_no_flatten_when_not_nested(self):
        blob = {"type": "llm", "provider": "openai"}
        result = _flatten_config_actor(blob)
        # No "config" key — returns shallow copy.
        assert "config" in blob  # original is unmodified
        assert result == dict(blob)

    def test_top_level_keys_take_precedence(self):
        blob = {
            "type": "tool",
            "name": "local/top",
            "config": {"actor": {"type": "llm", "provider": "gcp"}},
        }
        flat = _flatten_config_actor(blob)
        assert flat["type"] == "tool"  # top-level wins
        assert "config" not in flat

    def test_combined_format_without_slash_does_not_extract_provider_model(self):
        blob = {"name": "local/test", "config": {"actor": "gpt-4"}}
        flat = _flatten_config_actor(blob)
        # actor is a string but doesn't contain "/" — no provider/model extracted.
        assert "provider" not in flat
        assert "model" not in flat
        assert "config" not in flat  # wrapper still removed


class TestIsV3Yaml:
    """Tests for ``is_v3_yaml`` with combined-format config.actor."""

    def test_combined_format_string_is_v3(self):
        blob = {"name": "local/test", "config": {"actor": "aws/gpt-4"}}
        assert is_v3_yaml(blob) is True

    def test_combined_format_nested_dict_type_is_v3(self):
        blob = {
            "config": {"actor": {"type": "llm", "provider": "gcp"}},
            "name": "local/test",
        }
        assert is_v3_yaml(blob) is True

    def test_flat_top_level_llm_type_is_v3(self):
        blob = {"type": "llm", "name": "local/test"}
        assert is_v3_yaml(blob) is True

    def test_version_3_no_type_is_v3(self):
        blob = {"version": "3", "name": "local/test"}
        assert is_v3_yaml(blob) is True

    def test_null_type_not_v3(self):
        blob = {"type": None, "name": "local/test"}
        assert is_v3_yaml(blob) is False

    def test_non_dict_blob_not_v3(self):
        assert is_v3_yaml("string-blob") is False
        assert is_v3_yaml(None) is False

    def test_combined_format_without_slash_is_still_detected_as_v3(self):
        """A combined-format string without the slash is still a config-actor signal."""
        blob = {"config": {"actor": "gpt-4"}, "name": "local/test"}
        assert is_v3_yaml(blob) is True


class TestActorConfigurationFromBlobCombined:
    """Tests that ActorConfiguration.from_blob correctly handles combined format.

    These exercise the full flattening + canonicalization path in
    cleveragents.actor.config.ActorConfiguration.from_blob().
    """

    def test_from_blob_with_string_combined_format(self):
        from cleveragents.actor.config import ActorConfiguration

        blob = {
            "name": None,
            "config": {"actor": "gcp/vision-model"},
        }
        config = ActorConfiguration.from_blob(blob=blob, name="local/vision-ai")
        assert config.provider == "gcp"
        assert config.model == "vision-model"

    def test_from_blob_with_nested_dict_combined_format(self):
        from cleveragents.actor.config import ActorConfiguration

        blob = {
            "config": {"actor": {"provider": "anthropic", "model": "claude-haiku"}},
        }
        config = ActorConfiguration.from_blob(blob=blob, name="local/claude-assist")
        assert config.provider == "anthropic"
        assert config.model == "claude-haiku"
