"""
Integration test for the complete subscription creation flow
Tests user registration, subscription creation, tenant creation, and linking
"""

import pytest
import asyncio
import httpx
import stripe
import os
from datetime import datetime, timezone
from typing import Dict, Any, Optional


class SubscriptionCreationFlowTester:
    """Test the complete subscription creation flow"""

    def __init__(self):
        self.base_url = "https://bakery-ia.local"
        self.timeout = 30.0
        self.test_user_email = f"test_{datetime.now().strftime('%Y%m%d%H%M%S')}@example.com"
        self.test_user_password = "SecurePassword123!"
        self.test_user_full_name = "Test User"
        self.test_plan_id = "starter"  # Valid plans: starter, professional, enterprise
        self.test_payment_method_id = None  # Will be created dynamically

        # Initialize Stripe with API key from environment
        stripe_key = os.environ.get('STRIPE_SECRET_KEY')
        if stripe_key:
            stripe.api_key = stripe_key
            print(f"✅ Stripe initialized with test mode API key")
        else:
            print(f"⚠️  Warning: STRIPE_SECRET_KEY not found in environment")

        # Store created resources for cleanup
        self.created_user_id = None
        self.created_subscription_id = None
        self.created_tenant_id = None
        self.created_payment_method_id = None
    
    def _create_test_payment_method(self) -> str:
        """
        Create a real Stripe test payment method using Stripe's pre-made test tokens
        This simulates what happens in production when a user enters their card details

        In production: Frontend uses Stripe.js to tokenize card → creates PaymentMethod
        In testing: We use Stripe's pre-made test tokens (tok_visa, tok_mastercard, etc.)

        See: https://stripe.com/docs/testing#cards
        """
        try:
            print(f"💳 Creating Stripe test payment method...")

            # Use Stripe's pre-made test token tok_visa
            # This is the recommended approach for testing and mimics production flow
            # In production, Stripe.js creates a similar token from card details
            payment_method = stripe.PaymentMethod.create(
                type="card",
                card={"token": "tok_visa"}  # Stripe's pre-made test token
            )

            self.created_payment_method_id = payment_method.id
            print(f"✅ Created Stripe test payment method: {payment_method.id}")
            print(f"   This simulates a real card in production")
            return payment_method.id

        except Exception as e:
            print(f"❌ Failed to create payment method: {str(e)}")
            print(f"   Tip: Ensure raw card API is enabled in Stripe dashboard:")
            print(f"   https://dashboard.stripe.com/settings/integration")
            raise

    async def test_complete_flow(self):
        """Test the complete subscription creation flow"""
        print(f"🧪 Starting subscription creation flow test for {self.test_user_email}")

        try:
            # Step 0: Create a real Stripe test payment method
            # This is EXACTLY what happens in production when user enters card details
            self.test_payment_method_id = self._create_test_payment_method()
            print(f"✅ Step 0: Test payment method created")

            # Step 1: Register user with subscription
            user_data = await self._register_user_with_subscription()
            print(f"✅ Step 1: User registered successfully - user_id: {user_data['user']['id']}")
            
            # Step 2: Verify user was created in database
            await self._verify_user_in_database(user_data['user']['id'])
            print(f"✅ Step 2: User verified in database")
            
            # Step 3: Verify subscription was created (tenant-independent)
            subscription_data = await self._verify_subscription_created(user_data['user']['id'])
            print(f"✅ Step 3: Tenant-independent subscription verified - subscription_id: {subscription_data['subscription_id']}")
            
            # Step 4: Create tenant and link subscription
            tenant_data = await self._create_tenant_and_link_subscription(user_data['user']['id'], subscription_data['subscription_id'])
            print(f"✅ Step 4: Tenant created and subscription linked - tenant_id: {tenant_data['tenant_id']}")
            
            # Step 5: Verify subscription is linked to tenant
            await self._verify_subscription_linked_to_tenant(subscription_data['subscription_id'], tenant_data['tenant_id'])
            print(f"✅ Step 5: Subscription-tenant link verified")
            
            # Step 6: Verify tenant can access subscription
            await self._verify_tenant_subscription_access(tenant_data['tenant_id'])
            print(f"✅ Step 6: Tenant subscription access verified")
            
            print(f"🎉 All tests passed! Complete flow working correctly")
            return True
            
        except Exception as e:
            print(f"❌ Test failed: {str(e)}")
            return False
        
        finally:
            # Cleanup (optional - comment out if you want to inspect the data)
            # await self._cleanup_resources()
            pass
    
    async def _register_user_with_subscription(self) -> Dict[str, Any]:
        """Register a new user with subscription"""
        url = f"{self.base_url}/api/v1/auth/register-with-subscription"
        
        payload = {
            "email": self.test_user_email,
            "password": self.test_user_password,
            "full_name": self.test_user_full_name,
            "subscription_plan": self.test_plan_id,
            "payment_method_id": self.test_payment_method_id
        }
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            response = await client.post(url, json=payload)
            
            if response.status_code != 200:
                error_msg = f"User registration failed: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            result = response.json()
            self.created_user_id = result['user']['id']
            return result
    
    async def _verify_user_in_database(self, user_id: str):
        """Verify user was created in the database"""
        # This would be a direct database query in a real test
        # For now, we'll just check that the user ID is valid
        if not user_id or len(user_id) != 36:  # UUID should be 36 characters
            raise Exception(f"Invalid user ID: {user_id}")
        
        print(f"📋 User ID validated: {user_id}")
    
    async def _verify_subscription_created(self, user_id: str) -> Dict[str, Any]:
        """Verify that a tenant-independent subscription was created"""
        # Check the onboarding progress to see if subscription data was stored
        url = f"{self.base_url}/api/v1/auth/me/onboarding/progress"
        
        # Get access token for the user
        access_token = await self._get_user_access_token()
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            headers = {"Authorization": f"Bearer {access_token}"}
            response = await client.get(url, headers=headers)
            
            if response.status_code != 200:
                error_msg = f"Failed to get onboarding progress: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            progress_data = response.json()
            
            # Check if subscription data is in the progress
            subscription_data = None
            for step in progress_data.get('steps', []):
                if step.get('step_name') == 'subscription':
                    subscription_data = step.get('step_data', {})
                    break
            
            if not subscription_data:
                raise Exception("No subscription data found in onboarding progress")
            
            # Store subscription ID for later steps
            subscription_id = subscription_data.get('subscription_id')
            if not subscription_id:
                raise Exception("No subscription ID found in onboarding progress")
            
            self.created_subscription_id = subscription_id
            
            return {
                'subscription_id': subscription_id,
                'plan_id': subscription_data.get('plan_id'),
                'payment_method_id': subscription_data.get('payment_method_id'),
                'billing_cycle': subscription_data.get('billing_cycle')
            }
    
    async def _get_user_access_token(self) -> str:
        """Get access token for the test user"""
        url = f"{self.base_url}/api/v1/auth/login"
        
        payload = {
            "email": self.test_user_email,
            "password": self.test_user_password
        }
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            response = await client.post(url, json=payload)
            
            if response.status_code != 200:
                error_msg = f"User login failed: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            result = response.json()
            return result['access_token']
    
    async def _create_tenant_and_link_subscription(self, user_id: str, subscription_id: str) -> Dict[str, Any]:
        """Create a tenant and link the subscription to it"""
        # This would typically be done during the onboarding flow
        # For testing purposes, we'll simulate this by calling the tenant service directly
        
        url = f"{self.base_url}/api/v1/tenants"
        
        # Get access token for the user
        access_token = await self._get_user_access_token()
        
        payload = {
            "name": f"Test Bakery {datetime.now().strftime('%Y%m%d%H%M%S')}",
            "description": "Test bakery for integration testing",
            "subscription_id": subscription_id,
            "user_id": user_id
        }
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            headers = {
                "Authorization": f"Bearer {access_token}",
                "Content-Type": "application/json"
            }
            response = await client.post(url, json=payload, headers=headers)
            
            if response.status_code != 201:
                error_msg = f"Tenant creation failed: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            result = response.json()
            self.created_tenant_id = result['id']
            
            return {
                'tenant_id': result['id'],
                'name': result['name'],
                'status': result['status']
            }
    
    async def _verify_subscription_linked_to_tenant(self, subscription_id: str, tenant_id: str):
        """Verify that the subscription is properly linked to the tenant"""
        url = f"{self.base_url}/api/v1/tenants/{tenant_id}/subscription/status"
        
        # Get access token for the user
        access_token = await self._get_user_access_token()
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            headers = {"Authorization": f"Bearer {access_token}"}
            response = await client.get(url, headers=headers)
            
            if response.status_code != 200:
                error_msg = f"Failed to get subscription status: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            subscription_status = response.json()
            
            # Verify that the subscription is active and linked to the tenant
            if subscription_status['status'] not in ['active', 'trialing']:
                raise Exception(f"Subscription status is {subscription_status['status']}, expected 'active' or 'trialing'")
            
            if subscription_status['tenant_id'] != tenant_id:
                raise Exception(f"Subscription linked to wrong tenant: {subscription_status['tenant_id']} != {tenant_id}")
            
            print(f"📋 Subscription status verified: {subscription_status['status']}")
            print(f"📋 Subscription linked to tenant: {subscription_status['tenant_id']}")
    
    async def _verify_tenant_subscription_access(self, tenant_id: str):
        """Verify that the tenant can access its subscription"""
        url = f"{self.base_url}/api/v1/tenants/{tenant_id}/subscription/details"
        
        # Get access token for the user
        access_token = await self._get_user_access_token()
        
        async with httpx.AsyncClient(timeout=self.timeout, verify=False) as client:
            headers = {"Authorization": f"Bearer {access_token}"}
            response = await client.get(url, headers=headers)
            
            if response.status_code != 200:
                error_msg = f"Failed to get active subscription: {response.status_code} - {response.text}"
                print(f"🚨 {error_msg}")
                raise Exception(error_msg)
            
            subscription_data = response.json()
            
            # Verify that the subscription data is complete
            required_fields = ['id', 'status', 'plan', 'current_period_start', 'current_period_end']
            for field in required_fields:
                if field not in subscription_data:
                    raise Exception(f"Missing required field in subscription data: {field}")
            
            print(f"📋 Active subscription verified for tenant {tenant_id}")
            print(f"📋 Subscription plan: {subscription_data['plan']}")
            print(f"📋 Subscription status: {subscription_data['status']}")
    
    async def _cleanup_resources(self):
        """Clean up test resources"""
        print("🧹 Cleaning up test resources...")
        
        # In a real test, you would delete the user, tenant, and subscription
        # For now, we'll just print what would be cleaned up
        print(f"Would delete user: {self.created_user_id}")
        print(f"Would delete subscription: {self.created_subscription_id}")
        print(f"Would delete tenant: {self.created_tenant_id}")


@pytest.mark.asyncio
async def test_subscription_creation_flow():
    """Test the complete subscription creation flow"""
    tester = SubscriptionCreationFlowTester()
    result = await tester.test_complete_flow()
    assert result is True, "Subscription creation flow test failed"


if __name__ == "__main__":
    # Run the test
    import asyncio
    
    print("🚀 Starting subscription creation flow integration test...")
    
    # Create and run the test
    tester = SubscriptionCreationFlowTester()
    
    # Run the test
    success = asyncio.run(tester.test_complete_flow())
    
    if success:
        print("\n🎉 Integration test completed successfully!")
        print("\nTest Summary:")
        print(f"✅ User registration with subscription")
        print(f"✅ User verification in database")
        print(f"✅ Tenant-independent subscription creation")
        print(f"✅ Tenant creation and subscription linking")
        print(f"✅ Subscription-tenant link verification")
        print(f"✅ Tenant subscription access verification")
        print(f"\nAll components working together correctly! 🚀")
    else:
        print("\n❌ Integration test failed!")
        exit(1)