"""Tests for vr describe commands."""

import pytest
from unittest.mock import patch, Mock

from typer.testing import CliRunner

from vr_cli.cli import app
from vr_cli.entities.agent import Agent
from vr_cli.entities.agent_function import AgentFunction
from vr_cli.entities.agent_environment import AgentEnvironment
from vr_cli.entities.agent_secret import AgentSecret
from vr_cli.entities.organization_phone_number import OrganizationPhoneNumber
from vr_cli.entities.organization_secret import OrganizationSecret
from vr_cli.entities.organization_telephony import OrganizationTelephony


runner = CliRunner()


# ============================================================================
# Describe Agent Tests
# ============================================================================


class TestDescribeAgent:
    """Tests for describe agent command."""

    def test_describe_agent_by_name(self, mock_agent):
        """Should describe agent by name."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "Test Agent"])

        assert result.exit_code == 0
        assert "Test Agent" in result.output
        assert "test-agent-id-123" in result.output
        assert "A test agent for unit testing" in result.output
        mock_repo.get_by_name_or_id.assert_called_once_with("Test Agent")

    def test_describe_agent_by_id(self, mock_agent):
        """Should describe agent by ID."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "test-agent-id-123"])

        assert result.exit_code == 0
        mock_repo.get_by_name_or_id.assert_called_once_with("test-agent-id-123")

    def test_describe_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = None
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_describe_agent_shows_all_sections(self, mock_agent):
        """Should show all information sections."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "Test Agent"])

        assert result.exit_code == 0
        assert "Basic Information" in result.output
        assert "Voice & Transport" in result.output
        assert "Organization" in result.output
        assert "Debug & Tracing" in result.output
        assert "Timestamps" in result.output

    def test_describe_agent_shows_voice_info(self):
        """Should show voice configuration."""
        agent = Agent(
            id="agent-1",
            name="Voice Agent",
            description="Agent with voice config",
            default_voice_id="voice-123",
            default_voice_name="Samantha",
            transport="websocket",
            telephony_id="tel-456",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "Voice Agent"])

        assert result.exit_code == 0
        assert "voice-123" in result.output
        assert "Samantha" in result.output
        assert "websocket" in result.output
        assert "tel-456" in result.output

    def test_describe_agent_shows_timestamps(self):
        """Should show timestamps."""
        agent = Agent(
            id="agent-1",
            name="Test Agent",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-02T00:00:00Z",
            last_activity_at="2024-01-03T00:00:00Z",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "Test Agent"])

        assert result.exit_code == 0
        assert "2024-01-01T00:00:00Z" in result.output
        assert "2024-01-02T00:00:00Z" in result.output
        assert "2024-01-03T00:00:00Z" in result.output

    def test_describe_agent_handles_none_values(self):
        """Should handle agents with None values gracefully."""
        agent = Agent(
            id="agent-1",
            name=None,
            description=None,
            default_voice_id=None,
            default_voice_name=None,
            transport=None,
            tracing_enabled=None,
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_repo_class:
            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = agent
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "agent", "agent-1"])

        assert result.exit_code == 0
        assert "N/A" in result.output


# ============================================================================
# Describe Function Tests
# ============================================================================


class TestDescribeFunction:
    """Tests for describe function command."""

    def test_describe_function_by_name(self, mock_agent, mock_function):
        """Should describe function by name."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = mock_function
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "Test Function"])

        assert result.exit_code == 0
        assert "Test Function" in result.output
        assert "test-function-id-123" in result.output
        assert "python" in result.output

    def test_describe_function_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["describe", "function", "nonexistent", "func"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_describe_function_not_found(self, mock_agent):
        """Should fail when function not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = None
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Function not found" in result.output

    def test_describe_function_shows_code_preview(self, mock_agent):
        """Should show code preview when code is available."""
        function = AgentFunction(
            id="func-1",
            agent_id="agent-1",
            name="code_function",
            code="def hello():\n    return 'Hello, World!'",
            language="python",
            strategy="cascade",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = function
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "code_function"])

        assert result.exit_code == 0
        assert "Code Preview" in result.output
        assert "def hello():" in result.output

    def test_describe_function_shows_test_data(self, mock_agent):
        """Should show test data when available."""
        function = AgentFunction(
            id="func-1",
            agent_id="agent-1",
            name="test_function",
            language="python",
            strategy="cascade",
            test_data={"input": "test", "expected": "output"},
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = function
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "test_function"])

        assert result.exit_code == 0
        assert "Test Data" in result.output

    def test_describe_function_shows_display_name(self, mock_agent):
        """Should show both name and display_name."""
        function = AgentFunction(
            id="func-1",
            agent_id="agent-1",
            name="internal_name",
            display_name="User Friendly Name",
            language="python",
            strategy="cascade",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = function
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "internal_name"])

        assert result.exit_code == 0
        assert "User Friendly Name" in result.output
        assert "internal_name" in result.output

    def test_describe_function_multifile(self, mock_agent):
        """Should show multifile status."""
        function = AgentFunction(
            id="func-1",
            agent_id="agent-1",
            name="multifile_function",
            language="python",
            strategy="cascade",
            is_multifile=True,
            code_path="/path/to/code.zip",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentFunctionRepository") as mock_func_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_func_repo = Mock()
            mock_func_repo.get_by_name_or_id.return_value = function
            mock_func_repo_class.return_value = mock_func_repo

            result = runner.invoke(app, ["describe", "function", "Test Agent", "multifile_function"])

        assert result.exit_code == 0
        assert "Yes" in result.output  # Is Multifile
        assert "/path/to/code.zip" in result.output


# ============================================================================
# Describe Environment Tests
# ============================================================================


class TestDescribeEnvironment:
    """Tests for describe environment command."""

    def test_describe_environment_by_name(self, mock_agent, mock_environment):
        """Should describe environment by name."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = mock_environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "Test Environment"])

        assert result.exit_code == 0
        assert "Test Environment" in result.output
        assert "test-env-id-123" in result.output

    def test_describe_environment_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class:
            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["describe", "environment", "nonexistent", "env"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_describe_environment_not_found(self, mock_agent):
        """Should fail when environment not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = None
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "nonexistent"])

        assert result.exit_code != 0
        assert "Environment not found" in result.output

    def test_describe_environment_shows_all_sections(self, mock_agent, mock_environment):
        """Should show all information sections."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = mock_environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "Test Environment"])

        assert result.exit_code == 0
        assert "Basic Information" in result.output
        assert "Speech-to-Text" in result.output
        assert "Recording" in result.output
        assert "Error Handling" in result.output
        assert "Audio Processing" in result.output
        assert "VAD & Advanced" in result.output
        assert "Timestamps" in result.output

    def test_describe_environment_shows_stt_config(self, mock_agent):
        """Should show STT configuration."""
        environment = AgentEnvironment(
            id="env-1",
            agent_id="agent-1",
            name="stt-env",
            stt_model="nova-3",
            stt_language="en-US",
            stt_endpointing=300,
            stt_prompt="Some prompt",
            stt_filter="profanity",
            stt_noise_reduction_type="adaptive",
            stt_prewarm_model=True,
            stt_auto_switch=False,
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "stt-env"])

        assert result.exit_code == 0
        assert "nova-3" in result.output
        assert "en-US" in result.output
        assert "300" in result.output
        assert "Some prompt" in result.output

    def test_describe_environment_shows_recording_config(self, mock_agent):
        """Should show recording configuration."""
        environment = AgentEnvironment(
            id="env-1",
            agent_id="agent-1",
            name="recording-env",
            recording_enabled=True,
            recording_location="s3://bucket/recordings",
            redaction_enabled=True,
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "recording-env"])

        assert result.exit_code == 0
        assert "s3://bucket/recordings" in result.output

    def test_describe_environment_shows_error_handling(self, mock_agent):
        """Should show error handling configuration."""
        environment = AgentEnvironment(
            id="env-1",
            agent_id="agent-1",
            name="error-env",
            error_fallback_timeout_seconds=30,
            error_fallback_time_window_seconds=60,
            error_fallback_occurrence_threshold=3,
            error_fallback_type="transfer",
            error_fallback_value="+1234567890",
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "error-env"])

        assert result.exit_code == 0
        assert "30" in result.output
        assert "60" in result.output
        assert "3" in result.output
        assert "transfer" in result.output
        assert "+1234567890" in result.output

    def test_describe_environment_debug_flag(self, mock_agent):
        """Should show debug flag status."""
        environment = AgentEnvironment(
            id="env-1",
            agent_id="agent-1",
            name="debug-env",
            is_debug=True,
        )

        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentEnvironmentRepository") as mock_env_repo_class:

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_env_repo = Mock()
            mock_env_repo.get_by_name_or_id.return_value = environment
            mock_env_repo_class.return_value = mock_env_repo

            result = runner.invoke(app, ["describe", "environment", "Test Agent", "debug-env"])

        assert result.exit_code == 0
        # Is Debug should show Yes
        assert "Yes" in result.output


# ============================================================================
# Help Output Tests
# ============================================================================


class TestDescribeHelp:
    """Tests for describe command help output."""

    def test_describe_help(self):
        """Should show help for describe command."""
        result = runner.invoke(app, ["describe", "--help"])

        assert result.exit_code == 0
        assert "agent" in result.output
        assert "function" in result.output
        assert "environment" in result.output

    def test_describe_agent_help(self):
        """Should show help for describe agent command."""
        result = runner.invoke(app, ["describe", "agent", "--help"])

        assert result.exit_code == 0
        assert "Agent name or ID" in result.output

    def test_describe_function_help(self):
        """Should show help for describe function command."""
        result = runner.invoke(app, ["describe", "function", "--help"])

        assert result.exit_code == 0
        assert "Agent name or ID" in result.output
        assert "Function name" in result.output or "name_or_id" in result.output

    def test_describe_environment_help(self):
        """Should show help for describe environment command."""
        result = runner.invoke(app, ["describe", "environment", "--help"])

        assert result.exit_code == 0
        assert "Agent name or ID" in result.output
        assert "Environment name" in result.output or "name_or_id" in result.output

    def test_describe_secret_help(self):
        """Should show help for describe secret command."""
        result = runner.invoke(app, ["describe", "secret", "--help"])

        assert result.exit_code == 0
        assert "Secret name or ID" in result.output
        assert "--agent" in result.output

    def test_describe_phonenumber_help(self):
        """Should show help for describe phonenumber command."""
        result = runner.invoke(app, ["describe", "phonenumber", "--help"])

        assert result.exit_code == 0
        assert "Phone number ID" in result.output

    def test_describe_telephony_help(self):
        """Should show help for describe telephony command."""
        result = runner.invoke(app, ["describe", "telephony", "--help"])

        assert result.exit_code == 0
        assert "Telephony provider name or ID" in result.output

    def test_describe_help_shows_all_commands(self):
        """Should list all describe subcommands in help."""
        result = runner.invoke(app, ["describe", "--help"])

        assert result.exit_code == 0
        assert "secret" in result.output
        assert "phonenumber" in result.output
        assert "telephony" in result.output


# ============================================================================
# Describe Secret Tests
# ============================================================================


class TestDescribeSecret:
    """Tests for describe secret command."""

    def test_describe_org_secret(self, mock_user, mock_organization_secret):
        """Should describe an organization secret."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationSecretRepository") as mock_repo_class, \
             patch("vr_cli.commands.describe.Path") as mock_path:

            mock_get_org.return_value = mock_user.organization_id

            mock_voicerun_dir = Mock()
            mock_voicerun_dir.exists.return_value = False
            mock_path.cwd.return_value.__truediv__.return_value = mock_voicerun_dir

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_organization_secret
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "secret", "ORG_API_KEY"])

        assert result.exit_code == 0
        assert "ORG_API_KEY" in result.output
        assert "test-org-secret-id-123" in result.output
        assert "Organization" in result.output

    def test_describe_agent_secret_with_flag(self, mock_user, mock_agent, mock_secret):
        """Should describe an agent secret when --agent is provided."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationSecretRepository") as mock_org_repo_class, \
             patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentSecretRepository") as mock_agent_secret_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            # Org repo returns nothing
            mock_org_repo = Mock()
            mock_org_repo.get_by_name_or_id.return_value = None
            mock_org_repo_class.return_value = mock_org_repo

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_agent_secret_repo = Mock()
            mock_agent_secret_repo.get_by_name_or_id.return_value = mock_secret
            mock_agent_secret_repo_class.return_value = mock_agent_secret_repo

            result = runner.invoke(app, ["describe", "secret", "API_KEY", "--agent", "Test Agent"])

        assert result.exit_code == 0
        assert "API_KEY" in result.output
        assert "test-secret-id-123" in result.output
        assert "Agent" in result.output

    def test_describe_secret_not_found(self, mock_user):
        """Should fail when secret not found."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationSecretRepository") as mock_repo_class, \
             patch("vr_cli.commands.describe.Path") as mock_path:

            mock_get_org.return_value = mock_user.organization_id

            mock_voicerun_dir = Mock()
            mock_voicerun_dir.exists.return_value = False
            mock_path.cwd.return_value.__truediv__.return_value = mock_voicerun_dir

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = None
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "secret", "nonexistent"])

        assert result.exit_code != 0
        assert "Secret not found" in result.output

    def test_describe_secret_no_organization(self):
        """Should fail when unable to determine organization."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = None

            result = runner.invoke(app, ["describe", "secret", "API_KEY"])

        assert result.exit_code != 0
        assert "Unable to determine organization" in result.output

    def test_describe_secret_agent_not_found(self, mock_user):
        """Should fail when --agent specifies a nonexistent agent."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_name_or_id.return_value = None
            mock_agent_repo_class.return_value = mock_agent_repo

            result = runner.invoke(app, ["describe", "secret", "API_KEY", "-a", "nonexistent"])

        assert result.exit_code != 0
        assert "Agent not found" in result.output

    def test_describe_secret_shows_timestamps(self, mock_user):
        """Should show timestamps."""
        secret = OrganizationSecret(
            id="s-1",
            organization_id="test-org-id-456",
            name="MY_SECRET",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-02T00:00:00Z",
        )

        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationSecretRepository") as mock_repo_class, \
             patch("vr_cli.commands.describe.Path") as mock_path:

            mock_get_org.return_value = mock_user.organization_id

            mock_voicerun_dir = Mock()
            mock_voicerun_dir.exists.return_value = False
            mock_path.cwd.return_value.__truediv__.return_value = mock_voicerun_dir

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = secret
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "secret", "MY_SECRET"])

        assert result.exit_code == 0
        assert "2024-01-01T00:00:00Z" in result.output
        assert "2024-01-02T00:00:00Z" in result.output

    def test_describe_secret_with_agent_lock(self, mock_user, mock_agent, mock_secret):
        """Should fall back to agent secret via agent.lock when org secret not found."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationSecretRepository") as mock_org_repo_class, \
             patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentSecretRepository") as mock_agent_secret_repo_class, \
             patch("vr_cli.commands.describe.Path") as mock_path, \
             patch("vr_cli.commands.describe.load_agent_lock") as mock_load_lock:

            mock_get_org.return_value = mock_user.organization_id

            mock_voicerun_dir = Mock()
            mock_voicerun_dir.exists.return_value = True
            mock_path.cwd.return_value.__truediv__.return_value = mock_voicerun_dir

            mock_load_lock.return_value = {"id": "test-agent-id-123"}

            # Org repo returns nothing
            mock_org_repo = Mock()
            mock_org_repo.get_by_name_or_id.return_value = None
            mock_org_repo_class.return_value = mock_org_repo

            mock_agent_repo = Mock()
            mock_agent_repo.get_by_id.return_value = mock_agent
            mock_agent_repo_class.return_value = mock_agent_repo

            mock_agent_secret_repo = Mock()
            mock_agent_secret_repo.get_by_name_or_id.return_value = mock_secret
            mock_agent_secret_repo_class.return_value = mock_agent_secret_repo

            result = runner.invoke(app, ["describe", "secret", "API_KEY"])

        assert result.exit_code == 0
        assert "API_KEY" in result.output
        assert "Agent" in result.output


# ============================================================================
# Describe Phone Number Tests
# ============================================================================


class TestDescribePhoneNumber:
    """Tests for describe phonenumber command."""

    def test_describe_phonenumber_success(self, mock_user, mock_phone_number):
        """Should describe a phone number by ID."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_id.return_value = mock_phone_number
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "phonenumber", "test-pn-id-123"])

        assert result.exit_code == 0
        assert "+15551234567" in result.output
        assert "test-pn-id-123" in result.output
        assert "Main Line" in result.output
        assert "555" in result.output
        assert "US" in result.output
        assert "test-tel-id-789" in result.output

    def test_describe_phonenumber_not_found(self, mock_user):
        """Should fail when phone number not found."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_id.return_value = None
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "phonenumber", "nonexistent"])

        assert result.exit_code != 0
        assert "Phone number not found" in result.output

    def test_describe_phonenumber_no_organization(self):
        """Should fail when unable to determine organization."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = None

            result = runner.invoke(app, ["describe", "phonenumber", "pn-123"])

        assert result.exit_code != 0
        assert "Unable to determine organization" in result.output

    def test_describe_phonenumber_shows_timestamps(self, mock_user, mock_phone_number):
        """Should show timestamps."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_id.return_value = mock_phone_number
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "phonenumber", "test-pn-id-123"])

        assert result.exit_code == 0
        assert "2024-01-01T00:00:00Z" in result.output
        assert "2024-01-02T00:00:00Z" in result.output

    def test_describe_phonenumber_handles_none_values(self, mock_user):
        """Should handle phone numbers with None values."""
        pn = OrganizationPhoneNumber(
            id="pn-1",
            phone_number=None,
            friendly_name=None,
            area_code=None,
            country_code=None,
            telephony_id=None,
        )

        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_id.return_value = pn
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "phonenumber", "pn-1"])

        assert result.exit_code == 0
        assert "N/A" in result.output


# ============================================================================
# Describe Telephony Tests
# ============================================================================


class TestDescribeTelephony:
    """Tests for describe telephony command."""

    def test_describe_telephony_by_name(self, mock_user, mock_telephony):
        """Should describe a telephony provider by name."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_telephony
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "telephony", "My Twilio"])

        assert result.exit_code == 0
        assert "My Twilio" in result.output
        assert "test-tel-id-789" in result.output
        assert "twilio" in result.output
        assert "test-org-id-456" in result.output
        mock_repo.get_by_name_or_id.assert_called_once_with("My Twilio")

    def test_describe_telephony_by_id(self, mock_user, mock_telephony):
        """Should describe a telephony provider by ID."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_telephony
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "telephony", "test-tel-id-789"])

        assert result.exit_code == 0
        mock_repo.get_by_name_or_id.assert_called_once_with("test-tel-id-789")

    def test_describe_telephony_not_found(self, mock_user):
        """Should fail when telephony provider not found."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = None
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "telephony", "nonexistent"])

        assert result.exit_code != 0
        assert "Telephony provider not found" in result.output

    def test_describe_telephony_no_organization(self):
        """Should fail when unable to determine organization."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = None

            result = runner.invoke(app, ["describe", "telephony", "test"])

        assert result.exit_code != 0
        assert "Unable to determine organization" in result.output

    def test_describe_telephony_shows_timestamps(self, mock_user, mock_telephony):
        """Should show timestamps."""
        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = mock_telephony
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "telephony", "My Twilio"])

        assert result.exit_code == 0
        assert "2024-01-01T00:00:00Z" in result.output
        assert "2024-01-02T00:00:00Z" in result.output

    def test_describe_telephony_handles_none_values(self, mock_user):
        """Should handle telephony with None values."""
        provider = OrganizationTelephony(
            id="tel-1",
            name=None,
            provider_type=None,
            organization_id=None,
        )

        with patch("vr_cli.commands.describe.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.describe.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.get_by_name_or_id.return_value = provider
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["describe", "telephony", "tel-1"])

        assert result.exit_code == 0
        assert "N/A" in result.output
