"""
Integration Tests for Tenant Deletion System
Tests the complete deletion flow across all 12 microservices
"""

import asyncio
import pytest
import httpx
import os
from typing import Dict, List, Any
from uuid import uuid4
from datetime import timedelta
import structlog

from shared.auth.jwt_handler import JWTHandler

logger = structlog.get_logger(__name__)


# Test Configuration
BASE_URLS = {
    "tenant": "http://tenant-service:8000/api/v1",
    "orders": "http://orders-service:8000/api/v1",
    "inventory": "http://inventory-service:8000/api/v1",
    "recipes": "http://recipes-service:8000/api/v1",
    "sales": "http://sales-service:8000/api/v1",
    "production": "http://production-service:8000/api/v1",
    "suppliers": "http://suppliers-service:8000/api/v1",
    "pos": "http://pos-service:8000/api/v1",
    "external": "http://external-service:8000/api/v1",
    "forecasting": "http://forecasting-service:8000/api/v1",
    "training": "http://training-service:8000/api/v1",
    "alert_processor": "http://alert-processor-service:8000/api/v1",
    "notification": "http://notification-service:8000/api/v1",
}

# Test tenant ID (use a real demo tenant from the system)
TEST_TENANT_ID = "dbc2128a-7539-470c-94b9-c1e37031bd77"  # Demo tenant


@pytest.fixture
async def service_token():
    """Get a service JWT token for authentication"""
    # Get JWT secret from environment or use default for testing
    jwt_secret = os.getenv("JWT_SECRET", "test-secret-key-for-integration-tests-only")

    # Create JWT handler
    jwt_handler = JWTHandler(secret_key=jwt_secret)

    # Generate service token with 1 hour expiration for tests
    token = jwt_handler.create_service_token(
        service_name="integration-test-service",
        expires_delta=timedelta(hours=1)
    )

    logger.info("Generated service token for integration tests")
    return token


@pytest.fixture
async def http_client():
    """Create async HTTP client"""
    async with httpx.AsyncClient(verify=False, timeout=30.0) as client:
        yield client


class TestIndividualServiceDeletion:
    """Test each service's deletion endpoint individually"""

    @pytest.mark.asyncio
    async def test_orders_service_preview(self, http_client, service_token):
        """Test Orders service deletion preview"""
        url = f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        assert "total_records" in data
        assert data["service"] == "orders"
        logger.info("orders.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_inventory_service_preview(self, http_client, service_token):
        """Test Inventory service deletion preview"""
        url = f"{BASE_URLS['inventory']}/inventory/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        assert "total_records" in data
        logger.info("inventory.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_recipes_service_preview(self, http_client, service_token):
        """Test Recipes service deletion preview"""
        url = f"{BASE_URLS['recipes']}/recipes/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        logger.info("recipes.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_forecasting_service_preview(self, http_client, service_token):
        """Test Forecasting service deletion preview"""
        url = f"{BASE_URLS['forecasting']}/forecasting/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        assert data["service"] == "forecasting"
        logger.info("forecasting.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_training_service_preview(self, http_client, service_token):
        """Test Training service deletion preview"""
        url = f"{BASE_URLS['training']}/training/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        assert data["service"] == "training"
        logger.info("training.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_notification_service_preview(self, http_client, service_token):
        """Test Notification service deletion preview"""
        url = f"{BASE_URLS['notification']}/notifications/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        assert response.status_code == 200
        data = response.json()
        assert "preview" in data
        assert data["service"] == "notification"
        logger.info("notification.preview_test.passed", data=data)

    @pytest.mark.asyncio
    async def test_all_services_preview_parallel(self, http_client, service_token):
        """Test all services' preview endpoints in parallel"""
        headers = {"Authorization": f"Bearer {service_token}"}

        # Define all preview URLs
        preview_urls = {
            "orders": f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview",
            "inventory": f"{BASE_URLS['inventory']}/inventory/tenant/{TEST_TENANT_ID}/deletion-preview",
            "recipes": f"{BASE_URLS['recipes']}/recipes/tenant/{TEST_TENANT_ID}/deletion-preview",
            "sales": f"{BASE_URLS['sales']}/sales/tenant/{TEST_TENANT_ID}/deletion-preview",
            "production": f"{BASE_URLS['production']}/production/tenant/{TEST_TENANT_ID}/deletion-preview",
            "suppliers": f"{BASE_URLS['suppliers']}/suppliers/tenant/{TEST_TENANT_ID}/deletion-preview",
            "pos": f"{BASE_URLS['pos']}/pos/tenant/{TEST_TENANT_ID}/deletion-preview",
            "external": f"{BASE_URLS['external']}/external/tenant/{TEST_TENANT_ID}/deletion-preview",
            "forecasting": f"{BASE_URLS['forecasting']}/forecasting/tenant/{TEST_TENANT_ID}/deletion-preview",
            "training": f"{BASE_URLS['training']}/training/tenant/{TEST_TENANT_ID}/deletion-preview",
            "alert_processor": f"{BASE_URLS['alert_processor']}/alerts/tenant/{TEST_TENANT_ID}/deletion-preview",
            "notification": f"{BASE_URLS['notification']}/notifications/tenant/{TEST_TENANT_ID}/deletion-preview",
        }

        # Make all requests in parallel
        tasks = [
            http_client.get(url, headers=headers)
            for url in preview_urls.values()
        ]

        responses = await asyncio.gather(*tasks, return_exceptions=True)

        # Analyze results
        results = {}
        for service, response in zip(preview_urls.keys(), responses):
            if isinstance(response, Exception):
                results[service] = {"status": "error", "error": str(response)}
            else:
                results[service] = {
                    "status": "success" if response.status_code == 200 else "failed",
                    "status_code": response.status_code,
                    "data": response.json() if response.status_code == 200 else None
                }

        # Log summary
        successful = sum(1 for r in results.values() if r["status"] == "success")
        logger.info("parallel_preview_test.completed",
                   total_services=len(results),
                   successful=successful,
                   failed=len(results) - successful,
                   results=results)

        # Assert at least 10 services responded successfully
        assert successful >= 10, f"Only {successful}/12 services responded successfully"

        return results


class TestOrchestratedDeletion:
    """Test the orchestrator's ability to delete across all services"""

    @pytest.mark.asyncio
    async def test_orchestrator_preview_all_services(self, http_client, service_token):
        """Test orchestrator can preview deletion across all services"""
        from services.auth.app.services.deletion_orchestrator import DeletionOrchestrator

        orchestrator = DeletionOrchestrator(auth_token=service_token)

        # Get preview from all services
        previews = {}
        for service_name, endpoint_template in orchestrator.SERVICE_DELETION_ENDPOINTS.items():
            url = endpoint_template.format(tenant_id=TEST_TENANT_ID) + "/deletion-preview"
            try:
                response = await http_client.get(
                    url,
                    headers={"Authorization": f"Bearer {service_token}"},
                    timeout=10.0
                )
                if response.status_code == 200:
                    previews[service_name] = response.json()
                else:
                    previews[service_name] = {"error": f"HTTP {response.status_code}"}
            except Exception as e:
                previews[service_name] = {"error": str(e)}

        logger.info("orchestrator.preview_test.completed",
                   services_count=len(previews),
                   previews=previews)

        # Calculate total records across all services
        total_records = 0
        for service, data in previews.items():
            if "total_records" in data:
                total_records += data["total_records"]

        logger.info("orchestrator.preview_test.total_records",
                   total_records=total_records,
                   services=len(previews))

        assert len(previews) == 12, "Should have previews from all 12 services"
        assert total_records >= 0, "Should have valid record counts"


class TestErrorHandling:
    """Test error handling and edge cases"""

    @pytest.mark.asyncio
    async def test_invalid_tenant_id(self, http_client, service_token):
        """Test deletion with invalid tenant ID"""
        invalid_tenant_id = str(uuid4())
        url = f"{BASE_URLS['orders']}/orders/tenant/{invalid_tenant_id}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)

        # Should succeed with zero counts for non-existent tenant
        assert response.status_code == 200
        data = response.json()
        assert data["total_records"] == 0

    @pytest.mark.asyncio
    async def test_unauthorized_access(self, http_client):
        """Test deletion without authentication"""
        url = f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview"

        response = await http_client.get(url)

        # Should be unauthorized
        assert response.status_code in [401, 403]

    @pytest.mark.asyncio
    async def test_service_timeout_handling(self, http_client, service_token):
        """Test handling of service timeouts"""
        # Use a very short timeout to simulate timeout
        async with httpx.AsyncClient(verify=False, timeout=0.001) as short_client:
            url = f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview"
            headers = {"Authorization": f"Bearer {service_token}"}

            with pytest.raises((httpx.TimeoutException, httpx.ConnectTimeout)):
                await short_client.get(url, headers=headers)


class TestDataIntegrity:
    """Test data integrity after deletion"""

    @pytest.mark.asyncio
    async def test_cascade_deletion_order(self, http_client, service_token):
        """Test that child records are deleted before parents"""
        # This would require creating test data and verifying deletion order
        # For now, we verify the preview shows proper counts

        url = f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview"
        headers = {"Authorization": f"Bearer {service_token}"}

        response = await http_client.get(url, headers=headers)
        assert response.status_code == 200

        data = response.json()
        preview = data.get("preview", {})

        # Verify we have counts for both parents and children
        # In orders service: order_items (child) and orders (parent)
        if preview.get("order_items", 0) > 0:
            assert preview.get("orders", 0) > 0, "If items exist, orders should exist"

        logger.info("cascade_deletion_test.passed", preview=preview)


class TestPerformance:
    """Test performance of deletion operations"""

    @pytest.mark.asyncio
    async def test_parallel_deletion_performance(self, http_client, service_token):
        """Test performance of parallel deletion across services"""
        import time

        headers = {"Authorization": f"Bearer {service_token}"}
        preview_urls = [
            f"{BASE_URLS['orders']}/orders/tenant/{TEST_TENANT_ID}/deletion-preview",
            f"{BASE_URLS['inventory']}/inventory/tenant/{TEST_TENANT_ID}/deletion-preview",
            f"{BASE_URLS['forecasting']}/forecasting/tenant/{TEST_TENANT_ID}/deletion-preview",
            f"{BASE_URLS['training']}/training/tenant/{TEST_TENANT_ID}/deletion-preview",
        ]

        # Test parallel execution
        start_time = time.time()
        tasks = [http_client.get(url, headers=headers) for url in preview_urls]
        responses = await asyncio.gather(*tasks, return_exceptions=True)
        parallel_duration = time.time() - start_time

        # Test sequential execution
        start_time = time.time()
        for url in preview_urls:
            await http_client.get(url, headers=headers)
        sequential_duration = time.time() - start_time

        logger.info("performance_test.completed",
                   parallel_duration=parallel_duration,
                   sequential_duration=sequential_duration,
                   speedup=sequential_duration / parallel_duration if parallel_duration > 0 else 0)

        # Parallel should be faster
        assert parallel_duration < sequential_duration, "Parallel execution should be faster"


# Helper function to run all tests
async def run_all_tests():
    """Run all integration tests"""
    import sys

    logger.info("integration_tests.starting")

    # Run pytest programmatically
    exit_code = pytest.main([
        __file__,
        "-v",
        "-s",
        "--tb=short",
        "--log-cli-level=INFO"
    ])

    logger.info("integration_tests.completed", exit_code=exit_code)
    sys.exit(exit_code)


if __name__ == "__main__":
    asyncio.run(run_all_tests())
