"""Tests for phone number assignment commands (get, create, delete, describe)."""

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_environment import AgentEnvironment
from vr_cli.entities.agent_phone_number_assignment import AgentPhoneNumberAssignment


runner = CliRunner()


# ============================================================================
# Get Assignments Tests
# ============================================================================


class TestGetAssignments:
    """Tests for get assignments command."""

    def test_get_assignments_lists_all(self, mock_agent, mock_assignments_list):
        """Should list all assignments for an agent."""
        with patch("vr_cli.commands.get.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.get.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get.return_value = mock_assignments_list
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["get", "assignments", "Test Agent"])

        assert result.exit_code == 0
        assert "assignment-1" in result.output
        assert "assignment-2" in result.output
        assert "pn-1" in result.output
        assert "pn-2" in result.output

    def test_get_assignments_shows_environment_id(self, mock_agent, mock_assignments_list):
        """Should show environment IDs in the table."""
        with patch("vr_cli.commands.get.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.get.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get.return_value = mock_assignments_list
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["get", "assignments", "Test Agent"])

        assert result.exit_code == 0
        assert "env-1" in result.output
        assert "env-2" in result.output

    def test_get_assignments_empty(self, mock_agent):
        """Should show message when no assignments found."""
        with patch("vr_cli.commands.get.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.get.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get.return_value = []
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["get", "assignments", "Test Agent"])

        assert result.exit_code == 0
        assert "No assignments found" in result.output

    def test_get_assignments_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.get.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, ["get", "assignments", "nonexistent"])

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

    def test_get_assignments_shows_created_at(self, mock_agent, mock_assignments_list):
        """Should show created_at timestamps."""
        with patch("vr_cli.commands.get.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.get.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get.return_value = mock_assignments_list
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["get", "assignments", "Test Agent"])

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

    def test_get_assignment_singular_alias(self, mock_agent):
        """Should work with singular assignment alias."""
        with patch("vr_cli.commands.get.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.get.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get.return_value = []
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["get", "assignment", "Test Agent"])

        assert result.exit_code == 0


# ============================================================================
# Create Assignment Tests
# ============================================================================


class TestCreateAssignment:
    """Tests for create assignment command."""

    def test_create_assignment_success(self, mock_agent, mock_environment):
        """Should create an assignment successfully."""
        created = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-123",
            created_at="2024-01-01T00:00:00Z",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = created
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-123"])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "new-assignment-id" in result.output
        assert "pn-123" in result.output

    def test_create_assignment_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.create.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, ["create", "assignment", "nonexistent", "env", "pn-123"])

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

    def test_create_assignment_environment_not_found(self, mock_agent):
        """Should fail when environment not found."""
        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.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, ["create", "assignment", "Test Agent", "nonexistent", "pn-123"])

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

    def test_create_assignment_api_failure(self, mock_agent, mock_environment):
        """Should fail when API returns error."""
        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = None
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-123"])

        assert result.exit_code != 0
        assert "Failed to create assignment" in result.output

    def test_create_assignment_passes_correct_data(self, mock_agent, mock_environment):
        """Should pass correct phone number ID and environment ID to repository."""
        created = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-999",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = created
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-999"])

        assert result.exit_code == 0
        call_args = mock_assign_repo.create.call_args
        assignment_arg = call_args[0][0]
        assert assignment_arg.organization_phone_number_id == "pn-999"
        assert assignment_arg.agent_environment_id == "test-env-id-123"

    def test_create_assignment_with_configure(self, mock_agent, mock_environment):
        """Should configure the phone number after creating with --configure flag."""
        created = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-123",
        )
        configured = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-123",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = created
            mock_assign_repo.configure.return_value = configured
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-123", "--configure"])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "configured successfully" in result.output
        mock_assign_repo.create.assert_called_once()
        mock_assign_repo.configure.assert_called_once_with("new-assignment-id")

    def test_create_assignment_without_configure_does_not_call_configure(self, mock_agent, mock_environment):
        """Should not call configure when --configure flag is not set."""
        created = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-123",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = created
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-123"])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "configured successfully" not in result.output
        mock_assign_repo.configure.assert_not_called()

    def test_create_assignment_configure_api_failure(self, mock_agent, mock_environment):
        """Should fail when configure returns None after successful create."""
        created = AgentPhoneNumberAssignment(
            id="new-assignment-id",
            agent_id="test-agent-id-123",
            agent_environment_id="test-env-id-123",
            organization_phone_number_id="pn-123",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentEnvironmentRepository") as mock_env_repo_class, \
             patch("vr_cli.commands.create.AgentPhoneNumberAssignmentRepository") as mock_assign_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

            mock_assign_repo = Mock()
            mock_assign_repo.create.return_value = created
            mock_assign_repo.configure.return_value = None
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["create", "assignment", "Test Agent", "Test Environment", "pn-123", "--configure"])

        assert result.exit_code != 0
        assert "created successfully" in result.output
        assert "Failed to configure phone number" in result.output


# ============================================================================
# Delete Assignment Tests
# ============================================================================


class TestDeleteAssignment:
    """Tests for delete assignment command."""

    def test_delete_assignment_success(self, mock_agent):
        """Should delete an assignment successfully."""
        with patch("vr_cli.commands.delete.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.delete.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.delete_by_id.return_value = True
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["delete", "assignment", "Test Agent", "assignment-123"])

        assert result.exit_code == 0
        assert "deleted successfully" in result.output
        assert "assignment-123" in result.output
        mock_assign_repo.delete_by_id.assert_called_once_with("assignment-123")

    def test_delete_assignment_agent_not_found(self):
        """Should fail when agent not found."""
        with patch("vr_cli.commands.delete.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, ["delete", "assignment", "nonexistent", "assignment-123"])

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

    def test_delete_assignment_api_failure(self, mock_agent):
        """Should fail when API delete returns failure."""
        with patch("vr_cli.commands.delete.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.delete.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.delete_by_id.return_value = False
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["delete", "assignment", "Test Agent", "assignment-123"])

        assert result.exit_code != 0
        assert "Failed to delete assignment" in result.output


# ============================================================================
# Describe Assignment Tests
# ============================================================================


class TestDescribeAssignment:
    """Tests for describe assignment command."""

    def test_describe_assignment_success(self, mock_agent, mock_assignment):
        """Should describe an assignment by ID."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get_by_id.return_value = mock_assignment
            mock_assign_repo_class.return_value = mock_assign_repo

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

        assert result.exit_code == 0
        assert "test-assignment-id-123" in result.output
        assert "test-env-id-123" in result.output
        assert "test-pn-id-456" in result.output

    def test_describe_assignment_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", "assignment", "nonexistent", "assignment-123"])

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

    def test_describe_assignment_not_found(self, mock_agent):
        """Should fail when assignment not found."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get_by_id.return_value = None
            mock_assign_repo_class.return_value = mock_assign_repo

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

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

    def test_describe_assignment_shows_timestamps(self, mock_agent, mock_assignment):
        """Should show timestamps."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get_by_id.return_value = mock_assignment
            mock_assign_repo_class.return_value = mock_assign_repo

            result = runner.invoke(app, ["describe", "assignment", "Test Agent", "test-assignment-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_assignment_shows_sections(self, mock_agent, mock_assignment):
        """Should show all information sections."""
        with patch("vr_cli.commands.describe.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.describe.AgentPhoneNumberAssignmentRepository") as mock_assign_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_assign_repo = Mock()
            mock_assign_repo.get_by_id.return_value = mock_assignment
            mock_assign_repo_class.return_value = mock_assign_repo

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

        assert result.exit_code == 0
        assert "Assignment Details" in result.output
        assert "Timestamps" in result.output


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


class TestAssignmentHelp:
    """Tests for assignment command help output."""

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

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

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

        assert result.exit_code == 0
        assert "Agent name or ID" in result.output
        assert "Environment name or ID" in result.output
        assert "Organization phone number ID" in result.output
        assert "--configure" in result.output

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

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

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

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

    def test_get_help_includes_assignments(self):
        """Should show assignments in get help."""
        result = runner.invoke(app, ["get", "--help"])

        assert result.exit_code == 0
        assert "assignments" in result.output

    def test_create_help_includes_assignment(self):
        """Should show assignment in create help."""
        result = runner.invoke(app, ["create", "--help"])

        assert result.exit_code == 0
        assert "assignment" in result.output

    def test_delete_help_includes_assignment(self):
        """Should show assignment in delete help."""
        result = runner.invoke(app, ["delete", "--help"])

        assert result.exit_code == 0
        assert "assignment" in result.output

    def test_describe_help_includes_assignment(self):
        """Should show assignment in describe help."""
        result = runner.invoke(app, ["describe", "--help"])

        assert result.exit_code == 0
        assert "assignment" in result.output
