"""
Comprehensive onboarding flow tests.

Tests the complete signup and tenant provisioning flow including:
- Signup API validation
- Tenant creation (without legacy permission fields)
- Billing subscription creation
- Plan assignment based on tier/plan parameter
- Domain creation
- User creation
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
from rest_framework.test import APIRequestFactory
from rest_framework import status
from datetime import timedelta
from django.utils import timezone

from smoothschedule.identity.users import api_views


class TestSignupValidation:
    """Test signup API input validation."""

    def test_missing_subdomain_returns_400(self):
        """Signup without subdomain should return 400."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'email': 'user@test.com',
            'password': 'password123',
        })

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'Subdomain is required' in response.data['detail']

    @patch('smoothschedule.identity.users.api_views.Tenant')
    def test_existing_subdomain_returns_400(self, mock_tenant_model):
        """Signup with existing subdomain should return 400."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'existing',
            'email': 'user@test.com',
            'password': 'password123'
        })

        mock_tenant_model.objects.filter.return_value.exists.return_value = True

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'already taken' in response.data['detail']

    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.User')
    def test_existing_email_returns_400(self, mock_user_model, mock_tenant_model):
        """Signup with existing email should return 400."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'newbiz',
            'email': 'existing@test.com',
            'password': 'password123'
        })

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = True

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_400_BAD_REQUEST
        assert 'already exists' in response.data['detail']


class TestTenantCreation:
    """Test tenant creation during signup."""

    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_creates_tenant_without_legacy_fields(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan
    ):
        """Tenant creation should NOT include legacy permission fields."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'newbiz',
            'business_name': 'New Business',
            'email': 'owner@newbiz.com',
            'password': 'password123',
            'first_name': 'John',
            'last_name': 'Owner',
            'tier': 'GROWTH'
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.name = 'New Business'
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user.id = 10
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'signup-token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {'id': 10, 'email': 'owner@newbiz.com'}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")
        mock_plan.objects.filter.return_value.first.return_value = None

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify tenant was created WITHOUT legacy fields
        create_call = mock_tenant_model.objects.create.call_args
        create_kwargs = create_call[1]

        # These fields should NOT be in the create call
        assert 'subscription_tier' not in create_kwargs
        assert 'can_accept_payments' not in create_kwargs
        assert 'can_use_sms_reminders' not in create_kwargs
        assert 'can_use_custom_domain' not in create_kwargs
        assert 'can_api_access' not in create_kwargs
        assert 'can_use_masked_phone_numbers' not in create_kwargs
        assert 'can_white_label' not in create_kwargs

        # These fields SHOULD be in the create call
        assert create_kwargs['schema_name'] == 'newbiz'
        assert create_kwargs['name'] == 'New Business'
        assert create_kwargs['contact_email'] == 'owner@newbiz.com'

    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_creates_domain_for_tenant(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan
    ):
        """Signup should create a domain for the new tenant."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'newbiz',
            'business_name': 'New Business',
            'email': 'owner@newbiz.com',
            'password': 'password123',
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")
        mock_plan.objects.filter.return_value.first.return_value = None

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify domain was created
        mock_domain_model.objects.create.assert_called_once()
        domain_call = mock_domain_model.objects.create.call_args
        assert domain_call[1]['domain'] == 'newbiz.lvh.me'
        assert domain_call[1]['tenant'] == mock_tenant
        assert domain_call[1]['is_primary'] is True


class TestBillingSubscriptionCreation:
    """Test billing subscription creation during signup."""

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_creates_subscription_for_free_plan(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model, mock_subscription
    ):
        """Signup with free plan should create active subscription."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'freebiz',
            'business_name': 'Free Business',
            'email': 'owner@free.com',
            'password': 'password123',
            'plan': 'free'  # lowercase plan code
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        # Mock plan lookup
        mock_plan = Mock()
        mock_plan_version = Mock()
        mock_plan.versions.filter.return_value.order_by.return_value.first.return_value = mock_plan_version
        mock_plan_model.objects.get.return_value = mock_plan

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify subscription was created with 'active' status for free plan
        mock_subscription.objects.create.assert_called_once()
        sub_call = mock_subscription.objects.create.call_args
        assert sub_call[1]['business'] == mock_tenant
        assert sub_call[1]['plan_version'] == mock_plan_version
        assert sub_call[1]['status'] == 'active'  # Free plan is active, not trial
        assert sub_call[1]['trial_ends_at'] is None  # No trial for free

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_creates_trial_subscription_for_paid_plan(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model, mock_subscription
    ):
        """Signup with paid plan should create trial subscription."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'growthbiz',
            'business_name': 'Growth Business',
            'email': 'owner@growth.com',
            'password': 'password123',
            'tier': 'GROWTH'  # uppercase tier (frontend sends this)
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        # Mock plan lookup
        mock_plan = Mock()
        mock_plan_version = Mock()
        mock_plan.versions.filter.return_value.order_by.return_value.first.return_value = mock_plan_version
        mock_plan_model.objects.get.return_value = mock_plan

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify subscription was created with 'trial' status for paid plan
        mock_subscription.objects.create.assert_called_once()
        sub_call = mock_subscription.objects.create.call_args
        assert sub_call[1]['status'] == 'trial'  # Paid plans start as trial
        assert sub_call[1]['trial_ends_at'] is not None  # Has trial end date

    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_falls_back_to_free_plan_when_plan_not_found(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model
    ):
        """Signup should fall back to free plan when specified plan not found."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'invalidplan',
            'business_name': 'Invalid Plan Business',
            'email': 'owner@invalid.com',
            'password': 'password123',
            'plan': 'nonexistent_plan'
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan_model.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan_model.objects.get.side_effect = mock_plan_model.DoesNotExist("Plan not found")
        mock_plan_model.objects.filter.return_value.first.return_value = None

        # Should still succeed even if no plan found
        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED


class TestPlanCodeNormalization:
    """Test that plan codes are normalized correctly."""

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_accepts_uppercase_tier_from_frontend(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model, mock_subscription
    ):
        """Signup should accept uppercase 'tier' parameter from frontend."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'starterbiz',
            'email': 'owner@starter.com',
            'password': 'password123',
            'tier': 'STARTER'  # Frontend sends uppercase
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        mock_plan = Mock()
        mock_plan_version = Mock()
        mock_plan.versions.filter.return_value.order_by.return_value.first.return_value = mock_plan_version
        mock_plan_model.objects.get.return_value = mock_plan

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED
        # Plan lookup should use lowercase
        mock_plan_model.objects.get.assert_called_with(code='starter', is_active=True)

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_accepts_lowercase_plan_from_api(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model, mock_subscription
    ):
        """Signup should accept lowercase 'plan' parameter from API."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'probiz',
            'email': 'owner@pro.com',
            'password': 'password123',
            'plan': 'pro'  # API might send lowercase
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        mock_plan = Mock()
        mock_plan_version = Mock()
        mock_plan.versions.filter.return_value.order_by.return_value.first.return_value = mock_plan_version
        mock_plan_model.objects.get.return_value = mock_plan

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED
        mock_plan_model.objects.get.assert_called_with(code='pro', is_active=True)

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_defaults_to_free_when_no_plan_specified(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan_model, mock_subscription
    ):
        """Signup without plan should default to 'free'."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'defaultbiz',
            'email': 'owner@default.com',
            'password': 'password123',
            # No plan or tier specified
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        mock_plan = Mock()
        mock_plan_version = Mock()
        mock_plan.versions.filter.return_value.order_by.return_value.first.return_value = mock_plan_version
        mock_plan_model.objects.get.return_value = mock_plan

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED
        # Should default to free
        mock_plan_model.objects.get.assert_called_with(code='free', is_active=True)


class TestUserCreation:
    """Test user creation during signup."""

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_creates_tenant_owner_user(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan, mock_subscription
    ):
        """Signup should create user with TENANT_OWNER role."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'ownerbiz',
            'business_name': 'Owner Business',
            'email': 'owner@ownerbiz.com',
            'password': 'securepassword123',
            'first_name': 'Jane',
            'last_name': 'Owner',
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user.id = 10
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'auth-token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {'id': 10, 'email': 'owner@ownerbiz.com'}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify user was created with correct data
        mock_user_model.objects.create_user.assert_called_once()
        user_call = mock_user_model.objects.create_user.call_args
        assert user_call[1]['username'] == 'owner@ownerbiz.com'
        assert user_call[1]['email'] == 'owner@ownerbiz.com'
        assert user_call[1]['password'] == 'securepassword123'
        assert user_call[1]['first_name'] == 'Jane'
        assert user_call[1]['last_name'] == 'Owner'
        assert user_call[1]['role'] == 'TENANT_OWNER'
        assert user_call[1]['tenant'] == mock_tenant
        assert user_call[1]['email_verified'] is False


class TestSignupResponse:
    """Test signup API response format."""

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_returns_access_token(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan, mock_subscription
    ):
        """Signup should return access token."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'tokenbiz',
            'email': 'owner@token.com',
            'password': 'password123',
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.name = 'Token Business'
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'my-access-token-123'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {'id': 1, 'email': 'owner@token.com'}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED
        assert response.data['access'] == 'my-access-token-123'
        assert 'user' in response.data
        assert 'tenant' in response.data

    @patch('smoothschedule.billing.models.Subscription')
    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_returns_tenant_info(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan, mock_subscription
    ):
        """Signup should return tenant info with subdomain."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'infobiz',
            'business_name': 'Info Business',
            'email': 'owner@info.com',
            'password': 'password123',
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 42
        mock_tenant.name = 'Info Business'
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED
        assert response.data['tenant']['id'] == 42
        assert response.data['tenant']['name'] == 'Info Business'
        assert response.data['tenant']['subdomain'] == 'infobiz'
        assert response.data['tenant']['domain'] == 'infobiz.lvh.me'


class TestEmailVerificationOnSignup:
    """Test email verification is sent automatically on signup."""

    @patch('smoothschedule.billing.models.Plan')
    @patch('smoothschedule.identity.users.api_views._send_verification_email_for_user')
    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.Domain')
    @patch('smoothschedule.identity.users.api_views.User')
    @patch('smoothschedule.identity.users.api_views.Token')
    @patch('smoothschedule.identity.users.api_views._get_user_data')
    def test_sends_verification_email_on_signup(
        self, mock_get_user_data, mock_token_model, mock_user_model,
        mock_domain_model, mock_tenant_model, mock_schema_context,
        mock_send_email, mock_plan
    ):
        """Signup should automatically send verification email."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'emailbiz',
            'business_name': 'Email Test Business',
            'email': 'owner@emailtest.com',
            'password': 'password123',
            'first_name': 'Test',
            'last_name': 'User',
        })
        request.get_host = Mock(return_value='lvh.me:8000')

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False

        mock_tenant = Mock()
        mock_tenant.id = 1
        mock_tenant.name = 'Email Test Business'
        mock_tenant_model.objects.create.return_value = mock_tenant

        mock_user = Mock()
        mock_user.id = 10
        mock_user.email = 'owner@emailtest.com'
        mock_user.first_name = 'Test'
        mock_user_model.objects.create_user.return_value = mock_user
        mock_user_model.Role.TENANT_OWNER = 'TENANT_OWNER'

        mock_token = Mock()
        mock_token.key = 'token'
        mock_token_model.objects.get_or_create.return_value = (mock_token, True)

        mock_get_user_data.return_value = {'id': 10, 'email': 'owner@emailtest.com'}

        # Mock Plan.DoesNotExist properly so the except clause works
        mock_plan.DoesNotExist = type('DoesNotExist', (Exception,), {})
        mock_plan.objects.get.side_effect = mock_plan.DoesNotExist("Plan not found")
        mock_plan.objects.filter.return_value.first.return_value = None

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_201_CREATED

        # Verify email was sent
        mock_send_email.assert_called_once_with(mock_user, mock_tenant)

    @patch('smoothschedule.identity.users.api_views.EmailVerificationToken')
    @patch('smoothschedule.identity.users.api_views.send_mail')
    @patch('smoothschedule.identity.users.api_views.settings')
    def test_send_verification_email_helper_creates_token(
        self, mock_settings, mock_send_mail, mock_token_model
    ):
        """_send_verification_email_for_user should create a verification token."""
        mock_settings.DEBUG = True
        mock_settings.DEFAULT_FROM_EMAIL = 'test@smoothschedule.com'

        mock_user = Mock()
        mock_user.email = 'user@test.com'
        mock_user.first_name = 'Test'
        mock_user.full_name = 'Test User'

        mock_tenant = Mock()
        mock_tenant.schema_name = 'testbiz'
        mock_domain = Mock()
        mock_domain.domain = 'testbiz.lvh.me'
        mock_tenant.domains.filter.return_value.first.return_value = mock_domain

        mock_token = Mock()
        mock_token.token = 'verification-token-123'
        mock_token_model.create_for_user.return_value = mock_token

        # Call the helper function
        api_views._send_verification_email_for_user(mock_user, mock_tenant)

        # Verify token was created
        mock_token_model.create_for_user.assert_called_once_with(mock_user)

        # Verify email was sent
        mock_send_mail.assert_called_once()
        call_args = mock_send_mail.call_args
        assert 'Verify your email' in call_args[0][0]  # Subject
        assert 'verification-token-123' in call_args[0][1]  # Message contains token
        assert 'user@test.com' in call_args[0][3]  # To address

    @patch('smoothschedule.identity.users.api_views.EmailVerificationToken')
    @patch('smoothschedule.identity.users.api_views.send_mail')
    @patch('smoothschedule.identity.users.api_views.settings')
    def test_send_verification_email_uses_correct_url(
        self, mock_settings, mock_send_mail, mock_token_model
    ):
        """Verification email should use correct subdomain URL."""
        mock_settings.DEBUG = True
        mock_settings.DEFAULT_FROM_EMAIL = 'test@smoothschedule.com'

        mock_user = Mock()
        mock_user.email = 'user@test.com'
        mock_user.first_name = 'John'

        mock_tenant = Mock()
        mock_tenant.schema_name = 'mybusiness'
        mock_domain = Mock()
        mock_domain.domain = 'mybusiness.lvh.me'
        mock_tenant.domains.filter.return_value.first.return_value = mock_domain

        mock_token = Mock()
        mock_token.token = 'token-abc'
        mock_token_model.create_for_user.return_value = mock_token

        api_views._send_verification_email_for_user(mock_user, mock_tenant)

        # Verify the URL includes the subdomain
        call_args = mock_send_mail.call_args
        message_body = call_args[0][1]
        assert 'mybusiness.lvh.me:5173' in message_body
        assert 'verify-email?token=token-abc' in message_body

    @patch('smoothschedule.identity.users.api_views.EmailVerificationToken')
    @patch('smoothschedule.identity.users.api_views.send_mail')
    @patch('smoothschedule.identity.users.api_views.settings')
    def test_send_verification_email_handles_errors_gracefully(
        self, mock_settings, mock_send_mail, mock_token_model
    ):
        """Email errors should not crash signup (logged but not raised)."""
        mock_settings.DEBUG = True

        mock_user = Mock()
        mock_user.email = 'user@test.com'
        mock_user.first_name = 'Test'

        mock_tenant = Mock()
        mock_tenant.schema_name = 'testbiz'
        mock_tenant.domains.filter.return_value.first.return_value = None

        mock_token = Mock()
        mock_token.token = 'token'
        mock_token_model.create_for_user.return_value = mock_token

        # Simulate email sending failure
        mock_send_mail.side_effect = Exception("SMTP connection failed")

        # Should not raise exception
        api_views._send_verification_email_for_user(mock_user, mock_tenant)

        # Verify send_mail was called (and failed)
        mock_send_mail.assert_called_once()


class TestErrorHandling:
    """Test error handling in signup."""

    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.User')
    def test_handles_tenant_creation_error(
        self, mock_user_model, mock_tenant_model, mock_schema_context
    ):
        """Signup should handle tenant creation errors gracefully."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'errorbiz',
            'email': 'owner@error.com',
            'password': 'password123',
        })

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False
        mock_tenant_model.objects.create.side_effect = Exception("Database error")

        response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
        assert 'detail' in response.data

    @patch('smoothschedule.identity.users.api_views.schema_context')
    @patch('smoothschedule.identity.users.api_views.Tenant')
    @patch('smoothschedule.identity.users.api_views.User')
    def test_error_message_hidden_in_production(
        self, mock_user_model, mock_tenant_model, mock_schema_context
    ):
        """Error details should be hidden in production (DEBUG=False)."""
        factory = APIRequestFactory()
        request = factory.post('/api/auth/signup/', {
            'subdomain': 'prodbiz',
            'email': 'owner@prod.com',
            'password': 'password123',
        })

        mock_tenant_model.objects.filter.return_value.exists.return_value = False
        mock_user_model.objects.filter.return_value.exists.return_value = False
        mock_tenant_model.objects.create.side_effect = Exception("Secret database error")

        with patch('smoothschedule.identity.core.mixins.settings') as mock_settings:
            mock_settings.DEBUG = False

            response = api_views.signup_view(request)

        assert response.status_code == status.HTTP_500_INTERNAL_SERVER_ERROR
        # The actual error should not be exposed
        assert 'Secret database error' not in str(response.data.get('detail', ''))
