#!/usr/bin/env python3
"""
Test to verify that gudb NEVER makes remote calls in local mode.
This is a critical security guarantee.
"""
import os
import sys
import unittest
from unittest.mock import patch, MagicMock

# Ensure local mode
os.environ["GUDB_MODE"] = "local"
os.environ.pop("GUDB_API_ENDPOINT", None)

sys.path.insert(0, 'src')

import gudb
from gudb import monitor, Guardian, LocalProvider
from gudb.config import settings


class TestLocalOnlyGuarantee(unittest.TestCase):
    """Verify that local mode never makes network calls."""
    
    def setUp(self):
        """Reset environment for each test."""
        os.environ["GUDB_MODE"] = "local"
        os.environ.pop("GUDB_API_ENDPOINT", None)
        # Force reload settings
        from gudb import config
        config.settings = config.GudbConfig()
    
    def test_default_mode_is_local(self):
        """Verify that the default mode is 'local'."""
        from gudb import config
        fresh_settings = config.GudbConfig()
        self.assertEqual(fresh_settings.mode, "local")
        self.assertIsNone(fresh_settings.api_endpoint)
    
    def test_guardian_uses_local_provider_by_default(self):
        """Verify Guardian uses LocalProvider in local mode."""
        guardian = Guardian()
        self.assertIsInstance(guardian.provider, LocalProvider)
    
    def test_remote_mode_requires_endpoint(self):
        """Verify remote mode raises error without endpoint."""
        os.environ["GUDB_MODE"] = "remote"
        os.environ.pop("GUDB_API_ENDPOINT", None)
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        with self.assertRaises(RuntimeError) as ctx:
            Guardian()
        
        self.assertIn("GUDB_API_ENDPOINT", str(ctx.exception))
        self.assertIn("explicitly set", str(ctx.exception))
    
    def test_hybrid_mode_requires_endpoint(self):
        """Verify hybrid mode raises error without endpoint."""
        os.environ["GUDB_MODE"] = "hybrid"
        os.environ.pop("GUDB_API_ENDPOINT", None)
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        with self.assertRaises(RuntimeError) as ctx:
            Guardian()
        
        self.assertIn("GUDB_API_ENDPOINT", str(ctx.exception))
    
    @patch('requests.post')
    def test_local_mode_no_network_calls(self, mock_post):
        """Verify no HTTP requests are made in local mode."""
        os.environ["GUDB_MODE"] = "local"
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        # Create guardian and evaluate dangerous query
        guardian = Guardian()
        
        # Test dangerous query (should be caught by heuristics)
        result = guardian.evaluate("DELETE FROM users")
        self.assertEqual(result["verdict"], "STOP")
        
        # Test safe query
        result = guardian.evaluate("SELECT * FROM users WHERE id = 1")
        self.assertEqual(result["verdict"], "GO")
        
        # CRITICAL: Verify NO network calls were made
        mock_post.assert_not_called()
    
    def test_sqlite_integration_no_network(self):
        """Integration test: Verify SQLite operations don't trigger network calls."""
        try:
            import sqlite3
        except ImportError:
            self.skipTest("sqlite3 not available")
        
        os.environ["GUDB_MODE"] = "local"
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        with patch('requests.post') as mock_post:
            # Create in-memory database
            conn = sqlite3.connect(":memory:")
            
            # Wrap with gudb
            guarded_conn = monitor(conn)
            cur = guarded_conn.cursor()
            
            # Create table
            cur.execute("CREATE TABLE test (id INTEGER, name TEXT)")
            
            # Safe operations
            cur.execute("INSERT INTO test VALUES (1, 'Alice')")
            cur.execute("SELECT * FROM test")
            
            # Dangerous operation (should be blocked by heuristics)
            try:
                cur.execute("DELETE FROM test")  # No WHERE clause
            except Exception:
                pass  # Expected to be blocked
            
            # CRITICAL: No network calls should have been made
            mock_post.assert_not_called()
    
    def test_invalid_mode_raises_error(self):
        """Verify invalid mode raises clear error."""
        os.environ["GUDB_MODE"] = "invalid_mode"
        
        from gudb import config
        config.settings = config.GudbConfig(_env_file=None)
        
        with self.assertRaises(ValueError) as ctx:
            Guardian()
        
        # Should get validation error from pydantic
        self.assertIn("mode", str(ctx.exception).lower())
    
    def test_explicit_local_provider(self):
        """Verify explicit LocalProvider usage."""
        provider = LocalProvider()
        guardian = Guardian(provider=provider)
        
        # LocalProvider should always return GO (heuristics handle blocking)
        result = guardian.provider.evaluate("SELECT 1", {})
        self.assertEqual(result["verdict"], "GO")
    
    def test_local_mode_logging(self):
        """Verify local mode logs appropriate message."""
        import logging
        from io import StringIO
        
        # Capture logs
        log_capture = StringIO()
        handler = logging.StreamHandler(log_capture)
        handler.setLevel(logging.INFO)
        
        logger = logging.getLogger("guardian")
        logger.addHandler(handler)
        logger.setLevel(logging.INFO)
        
        os.environ["GUDB_MODE"] = "local"
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        # Create guardian (should log local mode message)
        Guardian()
        
        log_output = log_capture.getvalue()
        self.assertIn("Local Mode", log_output)
        self.assertIn("no remote calls", log_output.lower())
        
        logger.removeHandler(handler)


class TestModeTransitions(unittest.TestCase):
    """Test behavior when switching between modes."""
    
    def test_cannot_use_remote_endpoint_in_local_mode(self):
        """Even if endpoint is set, local mode should use LocalProvider."""
        os.environ["GUDB_MODE"] = "local"
        os.environ["GUDB_API_ENDPOINT"] = "https://example.com/api"
        
        from gudb import config
        config.settings = config.GudbConfig()
        
        guardian = Guardian()
        
        # Should still use LocalProvider
        self.assertIsInstance(guardian.provider, LocalProvider)


if __name__ == "__main__":
    # Run tests
    suite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__])
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Print summary
    print("\n" + "="*70)
    if result.wasSuccessful():
        print("✅ ALL TESTS PASSED - Local-only guarantee verified!")
        print("   - Default mode is 'local'")
        print("   - No network calls in local mode")
        print("   - Remote mode requires explicit endpoint")
        print("   - Invalid configurations raise errors")
    else:
        print("❌ TESTS FAILED - Security guarantee violated!")
        sys.exit(1)
    print("="*70)
