"""Unit tests for F5 BIG-IP Pydantic models."""

import json

import pytest


class TestF5Credentials:
    """Tests for F5Credentials model."""

    def test_from_env_valid(self, monkeypatch):
        """F5Credentials.from_env() loads credentials from environment."""
        monkeypatch.setenv("F5_USERNAME", "admin")
        monkeypatch.setenv("F5_PASSWORD", "secret")

        from mcp_network_jcd.models.f5 import F5Credentials

        creds = F5Credentials.from_env()
        assert creds.username == "admin"
        assert creds.password == "secret"

    def test_from_env_missing_username(self, monkeypatch):
        """F5Credentials.from_env() raises ValueError when F5_USERNAME is missing."""
        monkeypatch.delenv("F5_USERNAME", raising=False)
        monkeypatch.setenv("F5_PASSWORD", "secret")

        from mcp_network_jcd.models.f5 import F5Credentials

        with pytest.raises(ValueError, match="F5_USERNAME must be set"):
            F5Credentials.from_env()

    def test_from_env_missing_password(self, monkeypatch):
        """F5Credentials.from_env() raises ValueError when F5_PASSWORD is missing."""
        monkeypatch.setenv("F5_USERNAME", "admin")
        monkeypatch.delenv("F5_PASSWORD", raising=False)

        from mcp_network_jcd.models.f5 import F5Credentials

        with pytest.raises(ValueError, match="F5_PASSWORD must be set"):
            F5Credentials.from_env()

    def test_from_env_empty_username(self, monkeypatch):
        """F5Credentials.from_env() raises ValueError when F5_USERNAME is empty."""
        monkeypatch.setenv("F5_USERNAME", "")
        monkeypatch.setenv("F5_PASSWORD", "secret")

        from mcp_network_jcd.models.f5 import F5Credentials

        with pytest.raises(ValueError, match="F5_USERNAME must be set"):
            F5Credentials.from_env()

    def test_from_env_empty_password(self, monkeypatch):
        """F5Credentials.from_env() raises ValueError when F5_PASSWORD is empty."""
        monkeypatch.setenv("F5_USERNAME", "admin")
        monkeypatch.setenv("F5_PASSWORD", "")

        from mcp_network_jcd.models.f5 import F5Credentials

        with pytest.raises(ValueError, match="F5_PASSWORD must be set"):
            F5Credentials.from_env()

    def test_password_hidden_in_repr(self, monkeypatch):
        """Password is not shown in repr output."""
        monkeypatch.setenv("F5_USERNAME", "admin")
        monkeypatch.setenv("F5_PASSWORD", "supersecret")

        from mcp_network_jcd.models.f5 import F5Credentials

        creds = F5Credentials.from_env()
        assert "supersecret" not in repr(creds)


class TestF5Response:
    """Tests for F5Response model."""

    def test_success_response(self):
        """F5Response serializes a successful response."""
        from mcp_network_jcd.models.f5 import F5Response

        resp = F5Response(
            success=True,
            host="192.168.1.245",
            data={"kind": "tm:ltm:pool:poolstate", "name": "mypool"},
            duration_ms=150,
        )
        d = resp.model_dump()
        assert d["success"] is True
        assert d["host"] == "192.168.1.245"
        assert d["data"]["name"] == "mypool"
        assert d["duration_ms"] == 150
        assert "T" in d["timestamp"]
        assert d["timestamp"].endswith("Z")

    def test_error_response(self):
        """F5Response serializes an error response."""
        from mcp_network_jcd.models.f5 import F5Response
        from mcp_network_jcd.models.paloalto import ErrorInfo

        resp = F5Response(
            success=False,
            host="192.168.1.245",
            error=ErrorInfo(code="AUTH_FAILED", message="Invalid credentials"),
            duration_ms=50,
        )
        d = resp.model_dump()
        assert d["success"] is False
        assert d["error"]["code"] == "AUTH_FAILED"
        assert d["data"] is None

    def test_serializable_to_json(self):
        """F5Response can be serialized to valid JSON."""
        from mcp_network_jcd.models.f5 import F5Response

        resp = F5Response(
            success=True,
            host="10.0.0.1",
            data={"items": [{"name": "pool1"}]},
            duration_ms=100,
        )
        json_str = json.dumps(resp.model_dump())
        parsed = json.loads(json_str)
        assert parsed["success"] is True


class TestF5ObjectTypes:
    """Tests for F5_OBJECT_TYPES validation set."""

    def test_valid_object_types(self):
        """All expected object types are in the set."""
        from mcp_network_jcd.models.f5 import F5_OBJECT_TYPES

        assert "virtual" in F5_OBJECT_TYPES
        assert "pool" in F5_OBJECT_TYPES
        assert "irule" in F5_OBJECT_TYPES
        assert "profile" in F5_OBJECT_TYPES

    def test_invalid_object_type(self):
        """Invalid object types are not in the set."""
        from mcp_network_jcd.models.f5 import F5_OBJECT_TYPES

        assert "node" not in F5_OBJECT_TYPES
        assert "monitor" not in F5_OBJECT_TYPES


class TestF5SubCollections:
    """Tests for F5_SUBCOLLECTIONS mapping."""

    def test_pool_has_members(self):
        """Pool supports 'members' sub-collection."""
        from mcp_network_jcd.models.f5 import F5_SUBCOLLECTIONS

        assert "members" in F5_SUBCOLLECTIONS["pool"]

    def test_virtual_has_policies_and_profiles(self):
        """Virtual supports 'policies' and 'profiles' sub-collections."""
        from mcp_network_jcd.models.f5 import F5_SUBCOLLECTIONS

        assert "policies" in F5_SUBCOLLECTIONS["virtual"]
        assert "profiles" in F5_SUBCOLLECTIONS["virtual"]

    def test_irule_has_no_subcollections(self):
        """iRule has no sub-collections defined."""
        from mcp_network_jcd.models.f5 import F5_SUBCOLLECTIONS

        assert "irule" not in F5_SUBCOLLECTIONS

    def test_invalid_subcollection_not_in_pool(self):
        """Invalid sub-collection not accepted for pool."""
        from mcp_network_jcd.models.f5 import F5_SUBCOLLECTIONS

        assert "policies" not in F5_SUBCOLLECTIONS["pool"]
