"""Tests for vr create 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_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
from vr_cli.entities.user import User


runner = CliRunner()


# ============================================================================
# Create Agent Secret Tests
# ============================================================================


class TestCreateAgentSecret:
    """Tests for create agent secret command (with --agent flag)."""

    def test_create_agent_secret_success(self, mock_agent):
        """Should create an agent secret successfully."""
        created_secret = AgentSecret(
            id="new-secret-id",
            agent_id="test-agent-id-123",
            name="API_KEY",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:00:00Z",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentSecretRepository") as mock_secret_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_secret_repo = Mock()
            mock_secret_repo.create.return_value = created_secret
            mock_secret_repo_class.return_value = mock_secret_repo

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

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "API_KEY" in result.output
        assert "new-secret-id" in result.output
        assert "agent" in result.output.lower()

    def test_create_agent_secret_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", "secret", "API_KEY", "value", "--agent", "nonexistent"])

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

    def test_create_agent_secret_api_failure(self, mock_agent):
        """Should fail when API returns error."""
        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentSecretRepository") as mock_secret_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_secret_repo = Mock()
            mock_secret_repo.create.return_value = None
            mock_secret_repo_class.return_value = mock_secret_repo

            result = runner.invoke(app, ["create", "secret", "API_KEY", "value", "-a", "Test Agent"])

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

    def test_create_agent_secret_by_agent_id(self, mock_agent):
        """Should resolve agent by ID."""
        created_secret = AgentSecret(
            id="new-secret-id",
            agent_id="test-agent-id-123",
            name="DATABASE_URL",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentSecretRepository") as mock_secret_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_secret_repo = Mock()
            mock_secret_repo.create.return_value = created_secret
            mock_secret_repo_class.return_value = mock_secret_repo

            result = runner.invoke(app, ["create", "secret", "DATABASE_URL", "postgres://...", "--agent", "test-agent-id-123"])

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

    def test_create_agent_secret_passes_correct_data(self, mock_agent):
        """Should pass correct name and value to repository."""
        created_secret = AgentSecret(
            id="new-secret-id",
            agent_id="test-agent-id-123",
            name="MY_SECRET",
        )

        with patch("vr_cli.commands.create.AgentRepository") as mock_agent_repo_class, \
             patch("vr_cli.commands.create.AgentSecretRepository") as mock_secret_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_secret_repo = Mock()
            mock_secret_repo.create.return_value = created_secret
            mock_secret_repo_class.return_value = mock_secret_repo

            result = runner.invoke(app, ["create", "secret", "MY_SECRET", "my-secret-value", "--agent", "Test Agent"])

        assert result.exit_code == 0
        # Verify the secret was created with correct data
        call_args = mock_secret_repo.create.call_args
        secret_arg = call_args[0][0]
        assert secret_arg.name == "MY_SECRET"
        assert secret_arg.value == "my-secret-value"


# ============================================================================
# Create Organization Secret Tests
# ============================================================================


class TestCreateOrganizationSecret:
    """Tests for create organization secret command (without --agent flag)."""

    def test_create_org_secret_success(self, mock_user):
        """Should create an organization secret successfully."""
        created_secret = OrganizationSecret(
            id="new-org-secret-id",
            organization_id="test-org-id-456",
            name="ORG_API_KEY",
            created_at="2024-01-01T00:00:00Z",
            updated_at="2024-01-01T00:00:00Z",
        )

        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationSecretRepository") as mock_secret_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_secret_repo = Mock()
            mock_secret_repo.create.return_value = created_secret
            mock_secret_repo_class.return_value = mock_secret_repo

            result = runner.invoke(app, ["create", "secret", "ORG_API_KEY", "org-secret-value"])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "ORG_API_KEY" in result.output
        assert "new-org-secret-id" in result.output
        assert "Organization" in result.output

    def test_create_org_secret_no_user(self):
        """Should fail when user is not authenticated."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = None

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

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

    def test_create_org_secret_no_organization(self):
        """Should fail when user has no organization."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = None

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

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

    def test_create_org_secret_api_failure(self, mock_user):
        """Should fail when API returns error."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationSecretRepository") as mock_secret_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_secret_repo = Mock()
            mock_secret_repo.create.return_value = None
            mock_secret_repo_class.return_value = mock_secret_repo

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

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

    def test_create_org_secret_passes_correct_data(self, mock_user):
        """Should pass correct name and value to repository."""
        created_secret = OrganizationSecret(
            id="new-org-secret-id",
            organization_id="test-org-id-456",
            name="MY_ORG_SECRET",
        )

        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationSecretRepository") as mock_secret_repo_class:

            mock_get_org.return_value = mock_user.organization_id

            mock_secret_repo = Mock()
            mock_secret_repo.create.return_value = created_secret
            mock_secret_repo_class.return_value = mock_secret_repo

            result = runner.invoke(app, ["create", "secret", "MY_ORG_SECRET", "my-org-secret-value"])

        assert result.exit_code == 0
        # Verify the secret was created with correct data
        call_args = mock_secret_repo.create.call_args
        secret_arg = call_args[0][0]
        assert secret_arg.name == "MY_ORG_SECRET"
        assert secret_arg.value == "my-org-secret-value"


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


class TestCreateHelp:
    """Tests for create command help output."""

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

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

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

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

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

        assert result.exit_code == 0
        assert "Telephony provider ID" in result.output
        assert "--purchase" in result.output
        assert "--area-code" in result.output
        assert "--country-code" in result.output
        assert "--friendly-name" in result.output

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

        assert result.exit_code == 0
        assert "--name" in result.output
        assert "--provider-type" in result.output


# ============================================================================
# Create Phone Number Tests
# ============================================================================


class TestCreatePhoneNumber:
    """Tests for create phonenumber command."""

    def test_create_phonenumber_default(self, mock_user):
        """Should create a phone number without --purchase (standard create)."""
        created = OrganizationPhoneNumber(
            id="new-pn-id",
            organization_id="test-org-id-456",
            phone_number="+15559999999",
            friendly_name="New Line",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.create.return_value = created
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["create", "phonenumber", "tel-123", "-p", "+15559999999"])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "+15559999999" in result.output
        assert "new-pn-id" in result.output
        mock_repo.create.assert_called_once()
        mock_repo.purchase.assert_not_called()

    def test_create_phonenumber_default_api_failure(self, mock_user):
        """Should fail when create returns None."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

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

            result = runner.invoke(app, ["create", "phonenumber", "tel-123"])

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

    def test_create_phonenumber_purchase_success(self, mock_user):
        """Should purchase a phone number with --purchase flag."""
        purchased = OrganizationPhoneNumber(
            id="new-pn-id",
            organization_id="test-org-id-456",
            phone_number="+15559999999",
            friendly_name="New Line",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.purchase.return_value = purchased
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["create", "phonenumber", "tel-123", "--purchase"])

        assert result.exit_code == 0
        assert "purchased successfully" in result.output
        assert "+15559999999" in result.output
        assert "new-pn-id" in result.output
        mock_repo.purchase.assert_called_once()
        mock_repo.create.assert_not_called()

    def test_create_phonenumber_purchase_with_options(self, mock_user):
        """Should pass area code, country code, and friendly name to purchase."""
        purchased = OrganizationPhoneNumber(
            id="new-pn-id",
            phone_number="+15559999999",
            friendly_name="Sales Line",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.purchase.return_value = purchased
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, [
                "create", "phonenumber", "tel-123",
                "--purchase",
                "--area-code", "415",
                "--country-code", "US",
                "--friendly-name", "Sales Line",
            ])

        assert result.exit_code == 0
        mock_repo.purchase.assert_called_once_with(
            telephony_id="tel-123",
            area_code="415",
            country_code="US",
            friendly_name="Sales Line",
        )

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

            result = runner.invoke(app, ["create", "phonenumber", "tel-123"])

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

    def test_create_phonenumber_purchase_api_failure(self, mock_user):
        """Should fail when purchase returns None."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationPhoneNumberRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

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

            result = runner.invoke(app, ["create", "phonenumber", "tel-123", "--purchase"])

        assert result.exit_code != 0
        assert "Failed to purchase phone number" in result.output

    def test_create_phonenumber_shows_friendly_name(self, mock_user):
        """Should show friendly name when present."""
        purchased = OrganizationPhoneNumber(
            id="new-pn-id",
            phone_number="+15559999999",
            friendly_name="My Line",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.purchase.return_value = purchased
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, ["create", "phonenumber", "tel-123", "--purchase", "-n", "My Line"])

        assert result.exit_code == 0
        assert "My Line" in result.output


# ============================================================================
# Create Telephony Tests
# ============================================================================


class TestCreateTelephony:
    """Tests for create telephony command."""

    def test_create_telephony_twilio_success(self, mock_user):
        """Should create a Twilio telephony provider with all flags."""
        created = OrganizationTelephony(
            id="new-tel-id",
            organization_id="test-org-id-456",
            name="My Twilio",
            provider_type="twilio",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.create_with_credentials.return_value = created
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, [
                "create", "telephony",
                "--name", "My Twilio",
                "--provider-type", "twilio",
                "--account-sid", "AC123",
                "--api-key-sid", "SK123",
                "--api-key-secret", "secret123",
            ])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "My Twilio" in result.output
        assert "new-tel-id" in result.output
        assert "twilio" in result.output
        mock_repo.create_with_credentials.assert_called_once_with(
            "My Twilio", "twilio",
            {"accountSid": "AC123", "apiKeySid": "SK123", "apiKeySecret": "secret123"},
        )

    def test_create_telephony_telnyx_success(self, mock_user):
        """Should create a Telnyx telephony provider with all flags."""
        created = OrganizationTelephony(
            id="new-tel-id",
            name="My Telnyx",
            provider_type="telnyx",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            mock_repo = Mock()
            mock_repo.create_with_credentials.return_value = created
            mock_repo_class.return_value = mock_repo

            result = runner.invoke(app, [
                "create", "telephony",
                "--name", "My Telnyx",
                "--provider-type", "telnyx",
                "--api-key", "key123",
            ])

        assert result.exit_code == 0
        assert "created successfully" in result.output
        assert "My Telnyx" in result.output
        mock_repo.create_with_credentials.assert_called_once_with(
            "My Telnyx", "telnyx",
            {"apiKey": "key123"},
        )

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

            result = runner.invoke(app, [
                "create", "telephony",
                "--name", "Test",
                "--provider-type", "twilio",
                "--account-sid", "AC123",
                "--api-key-sid", "SK123",
                "--api-key-secret", "secret123",
            ])

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

    def test_create_telephony_invalid_provider_type(self, mock_user):
        """Should fail with invalid provider type."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org:
            mock_get_org.return_value = mock_user.organization_id

            result = runner.invoke(app, [
                "create", "telephony",
                "--name", "Test",
                "--provider-type", "invalid",
            ])

        assert result.exit_code != 0
        assert "Invalid provider type" in result.output

    def test_create_telephony_api_failure(self, mock_user):
        """Should fail when API returns None."""
        with patch("vr_cli.commands.create.get_effective_organization_id") as mock_get_org, \
             patch("vr_cli.commands.create.OrganizationTelephonyRepository") as mock_repo_class:

            mock_get_org.return_value = mock_user.organization_id

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

            result = runner.invoke(app, [
                "create", "telephony",
                "--name", "Test",
                "--provider-type", "twilio",
                "--account-sid", "AC123",
                "--api-key-sid", "SK123",
                "--api-key-secret", "secret123",
            ])

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

    def test_create_telephony_interactive_prompts(self, mock_user):
        """Should prompt interactively when flags are missing."""
        created = OrganizationTelephony(
            id="new-tel-id",
            name="Prompted",
            provider_type="telnyx",
        )

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

            mock_get_org.return_value = mock_user.organization_id

            # Simulate interactive prompts: name, provider_type, api_key
            mock_prompt.side_effect = ["Prompted", "telnyx", "key123"]

            mock_repo = Mock()
            mock_repo.create_with_credentials.return_value = created
            mock_repo_class.return_value = mock_repo

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

        assert result.exit_code == 0
        assert "created successfully" in result.output
