#!/usr/bin/env python3
"""
Integration tests for local file loading and custom auth headers.
"""
import os
import sys
import asyncio

# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

from config import ServerConfig
from auth import AuthenticationManager
from openapi_loader import OpenAPILoader
from fastmcp_server import FastMCPOpenAPIServer


async def test_local_spec_with_custom_auth():
    """Test loading local spec and using custom auth headers."""
    print("Testing local spec with custom auth headers...")
    
    try:
        # Set up environment
        os.environ["OPENAPI_URL"] = "./test/fixtures/petstore.json"
        os.environ["SERVER_NAME"] = "test_petstore"
        os.environ["MCP_AUTH_HEADERS"] = '{"X-API-Key": "test-key-123", "X-Client-ID": "test-client"}'
        
        config = ServerConfig()
        
        # Verify local spec loading
        assert not config.openapi_url.startswith("http"), "Should be local file"
        
        # Verify custom headers
        assert config.has_custom_headers(), "Should have custom headers"
        assert config.auth_headers["X-API-Key"] == "test-key-123", "API key should match"
        
        # Create and initialize server
        server = FastMCPOpenAPIServer(config)
        await server.initialize()
        
        # Verify spec was loaded
        assert server.openapi_spec is not None, "Spec should be loaded"
        assert server.openapi_spec["info"]["title"] == "Test Petstore API", "Title should match"
        
        # Verify operations were registered
        assert len(server.operations) > 0, "Should have operations"
        operation_ids = [op.operation_id for op in server.operations]
        assert "listPets" in operation_ids, "Should have listPets operation"
        
        # Verify auth manager has custom headers
        auth_headers = server.authenticator.get_custom_headers()
        assert "X-API-Key" in auth_headers, "Should have API key in auth"
        
        print("✓ Local spec with custom auth successful")
        return True
        
    except Exception as e:
        print(f"✗ Local spec with custom auth failed: {e}")
        import traceback
        traceback.print_exc()
        return False
    finally:
        # Clean up environment
        os.environ.pop("MCP_AUTH_HEADERS", None)


async def test_weather_api_with_local_spec():
    """Test weather API using local OpenAPI spec."""
    print("Testing weather API with local spec...")
    
    try:
        # Set up environment for local weather spec
        os.environ["OPENAPI_URL"] = "./test/fixtures/weather.yaml"
        os.environ["SERVER_NAME"] = "test_weather"
        os.environ.pop("MCP_AUTH_HEADERS", None)  # No auth for this test
        
        config = ServerConfig()
        server = FastMCPOpenAPIServer(config)
        await server.initialize()
        
        # Verify YAML spec was loaded
        assert server.openapi_spec["info"]["title"] == "Test Weather API", "Title should match"
        assert server.openapi_spec["info"]["version"] == "1.0.0", "Version should match"
        
        # Verify weather operations
        operation_ids = [op.operation_id for op in server.operations]
        assert "getForecast" in operation_ids, "Should have getForecast"
        assert "getCurrentWeather" in operation_ids, "Should have getCurrentWeather"
        
        # Find getForecast operation
        forecast_op = next((op for op in server.operations if op.operation_id == "getForecast"), None)
        assert forecast_op is not None, "Should find getForecast operation"
        
        # Verify parameters
        param_names = [p.get("name") for p in forecast_op.parameters]
        assert "lat" in param_names, "Should have lat parameter"
        assert "lon" in param_names, "Should have lon parameter"
        
        print("✓ Weather API with local spec successful")
        return True
        
    except Exception as e:
        print(f"✗ Weather API with local spec failed: {e}")
        import traceback
        traceback.print_exc()
        return False


async def test_multiple_auth_methods():
    """Test custom headers combined with OAuth configuration."""
    print("Testing multiple auth methods...")
    
    try:
        # Set up environment with both custom headers and OAuth
        os.environ["OPENAPI_URL"] = "./test/fixtures/petstore.json"
        os.environ["SERVER_NAME"] = "test_multi_auth"
        os.environ["MCP_AUTH_HEADERS"] = '{"X-API-Key": "custom-key"}'
        os.environ["OAUTH_CLIENT_ID"] = "test-client"
        os.environ["OAUTH_CLIENT_SECRET"] = "test-secret"
        os.environ["OAUTH_TOKEN_URL"] = "https://test.com/oauth/token"
        
        config = ServerConfig()
        
        # Verify both auth methods are configured
        assert config.has_custom_headers(), "Should have custom headers"
        assert config.is_oauth_configured(), "Should have OAuth configured"
        
        # Create auth manager
        auth_manager = AuthenticationManager(config)
        assert auth_manager.is_configured(), "Auth manager should be configured"
        
        # Test header application
        request_headers = {"Content-Type": "application/json"}
        updated = auth_manager.add_auth_headers(request_headers)
        
        # Custom headers should be applied
        assert "X-API-Key" in updated, "Should have API key"
        assert updated["X-API-Key"] == "custom-key", "API key should match"
        assert "Content-Type" in updated, "Should preserve existing headers"
        
        print("✓ Multiple auth methods work together")
        return True
        
    except Exception as e:
        print(f"✗ Multiple auth methods test failed: {e}")
        return False
    finally:
        # Clean up environment
        os.environ.pop("MCP_AUTH_HEADERS", None)
        os.environ.pop("OAUTH_CLIENT_ID", None)
        os.environ.pop("OAUTH_CLIENT_SECRET", None)
        os.environ.pop("OAUTH_TOKEN_URL", None)


async def test_relative_path_spec_loading():
    """Test loading spec from different relative paths."""
    print("Testing relative path spec loading...")
    
    try:
        original_dir = os.getcwd()
        
        # Test from project root
        os.environ["OPENAPI_URL"] = "./test/fixtures/weather.yaml"
        os.environ["SERVER_NAME"] = "test_relative"
        
        config = ServerConfig()
        auth_headers = None
        spec = OpenAPILoader.load_spec(config.openapi_url, auth_headers)
        
        assert spec["info"]["title"] == "Test Weather API", "Should load from project root"
        
        # Change to test directory and use different relative path
        os.chdir(os.path.dirname(__file__))
        os.environ["OPENAPI_URL"] = "./fixtures/petstore.json"
        
        config2 = ServerConfig()
        spec2 = OpenAPILoader.load_spec(config2.openapi_url, auth_headers)
        
        assert spec2["info"]["title"] == "Test Petstore API", "Should load from test dir"
        
        os.chdir(original_dir)
        
        print("✓ Relative path spec loading works")
        return True
        
    except Exception as e:
        print(f"✗ Relative path test failed: {e}")
        os.chdir(original_dir)
        return False


async def test_error_handling_invalid_local_spec():
    """Test error handling for invalid local spec."""
    print("Testing error handling for invalid local spec...")
    
    try:
        # Try to load invalid spec
        os.environ["OPENAPI_URL"] = "./test/fixtures/invalid.json"
        os.environ["SERVER_NAME"] = "test_invalid"
        
        config = ServerConfig()
        server = FastMCPOpenAPIServer(config)
        
        try:
            await server.initialize()
            print("✗ Should have raised error for invalid spec")
            return False
        except Exception as e:
            assert "Missing required properties" in str(e), "Should mention missing properties"
            print("✓ Invalid spec error handled correctly")
            return True
            
    except Exception as e:
        print(f"✗ Error handling test failed: {e}")
        return False


async def test_spec_with_auth_headers_loading():
    """Test that auth headers are passed when loading remote specs."""
    print("Testing auth headers passed to spec loading...")
    
    try:
        # This tests that the infrastructure is in place
        # We can't actually test remote loading without a real server
        os.environ["OPENAPI_URL"] = "./test/fixtures/petstore.json"  # Use local for testing
        os.environ["SERVER_NAME"] = "test_auth_load"
        os.environ["MCP_AUTH_HEADERS"] = '{"Authorization": "Bearer test-token"}'
        
        config = ServerConfig()
        server = FastMCPOpenAPIServer(config)
        
        # Verify authenticator is set up
        assert server.authenticator is not None, "Should have authenticator"
        
        # Get headers that would be passed
        custom_headers = server.authenticator.get_custom_headers()
        assert "Authorization" in custom_headers, "Should have Authorization header"
        
        # Initialize (this will pass headers to load_spec)
        await server.initialize()
        
        # If we made it here, headers were passed correctly
        print("✓ Auth headers infrastructure correct")
        return True
        
    except Exception as e:
        print(f"✗ Auth headers loading test failed: {e}")
        return False
    finally:
        os.environ.pop("MCP_AUTH_HEADERS", None)


async def run_all_tests():
    """Run all integration tests."""
    print("\n" + "="*50)
    print("Integration Tests - Local Files & Custom Auth")
    print("="*50 + "\n")
    
    tests = [
        test_local_spec_with_custom_auth,
        test_weather_api_with_local_spec,
        test_multiple_auth_methods,
        test_relative_path_spec_loading,
        test_error_handling_invalid_local_spec,
        test_spec_with_auth_headers_loading
    ]
    
    results = []
    for test in tests:
        try:
            success = await test()
            results.append(success)
        except Exception as e:
            print(f"✗ Test {test.__name__} crashed: {e}")
            results.append(False)
        print()  # Add blank line between tests
    
    # Summary
    passed = sum(results)
    total = len(results)
    
    print("="*50)
    print(f"Results: {passed}/{total} tests passed")
    
    if passed == total:
        print("✓ All integration tests passed!")
        return True
    else:
        print(f"✗ {total - passed} tests failed")
        return False


if __name__ == "__main__":
    success = asyncio.run(run_all_tests())
    sys.exit(0 if success else 1)