#!/usr/bin/env python3
"""Garlic Services - Enhanced Tor Services with Clove Security - Python 3.6 Compatible"""

import os
import sys
import time
import json
import hashlib
import hmac
import base64
import socket
import threading
import subprocess
from datetime import datetime, timezone

try:
    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.primitives import hashes, hmac as crypto_hmac, serialization
    from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
    from cryptography.hazmat.backends import default_backend
    CRYPTO_AVAILABLE = True
except ImportError:
    CRYPTO_AVAILABLE = False

class CloveSecurity:
    """Clove-based security layer for Garlic Services"""
    
    def __init__(self, master_key=None):
        self.master_key = master_key or self.generate_master_key()
        self.clove_layers = 3  # Multi-layer clove security
        if CRYPTO_AVAILABLE:
            self.backend = default_backend()
        
    def generate_master_key(self):
        """Generate cryptographically secure master key"""
        return os.urandom(32)
    
    def derive_clove_key(self, clove_id, purpose='encryption'):
        """Derive key for specific clove layer"""
        if not CRYPTO_AVAILABLE:
            # Fallback for systems without cryptography
            return hashlib.sha256(f'{self.master_key.hex()}-{clove_id}-{purpose}'.encode()).digest()
        
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=f'clove-{clove_id}-{purpose}'.encode(),
            iterations=100000,
            backend=self.backend
        )
        return kdf.derive(self.master_key)
    
    def encrypt_with_cloves(self, data, clove_id):
        """Encrypt data with multi-layer clove security"""
        if not CRYPTO_AVAILABLE:
            # Simple XOR fallback
            key = self.derive_clove_key(clove_id)
            return bytes([a ^ b for a, b in zip(data, key * (len(data) // 32 + 1))])
        
        encrypted = data
        for layer in range(self.clove_layers):
            key = self.derive_clove_key(f'{clove_id}-layer-{layer}')
            iv = os.urandom(16)
            cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=self.backend)
            encryptor = cipher.encryptor()
            
            # Pad data to AES block size
            padding_length = 16 - (len(encrypted) % 16)
            padded_data = encrypted + bytes([padding_length] * padding_length)
            
            encrypted = iv + encryptor.update(padded_data) + encryptor.finalize()
        
        return encrypted
    
    def decrypt_with_cloves(self, encrypted_data, clove_id):
        """Decrypt data with multi-layer clove security"""
        if not CRYPTO_AVAILABLE:
            # Simple XOR fallback
            key = self.derive_clove_key(clove_id)
            return bytes([a ^ b for a, b in zip(encrypted_data, key * (len(encrypted_data) // 32 + 1))])
        
        decrypted = encrypted_data
        
        for layer in reversed(range(self.clove_layers)):
            key = self.derive_clove_key(f'{clove_id}-layer-{layer}')
            iv = decrypted[:16]
            cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=self.backend)
            decryptor = cipher.decryptor()
            
            padded_data = decryptor.update(decrypted[16:]) + decryptor.finalize()
            padding_length = padded_data[-1]
            decrypted = padded_data[:-padding_length]
        
        return decrypted
    
    def create_clove_signature(self, data, clove_id):
        """Create cryptographic signature for clove"""
        key = self.derive_clove_key(clove_id, 'signature')
        if CRYPTO_AVAILABLE:
            h = crypto_hmac.HMAC(key, hashes.SHA256(), backend=self.backend)
            h.update(data)
            return h.finalize()
        else:
            # Fallback HMAC
            return hmac.new(key, data, hashlib.sha256).digest()
    
    def verify_clove_signature(self, data, signature, clove_id):
        """Verify clove signature"""
        expected = self.create_clove_signature(data, clove_id)
        return hmac.compare_digest(expected, signature)

class GarlicService:
    """Individual Garlic Service instance"""
    
    def __init__(self, service_id, service_port, clove_security):
        self.service_id = service_id
        self.service_port = service_port
        self.clove_security = clove_security
        self.onion_address = None
        self.garlic_address = None
        self.hidden_service_dir = f'/var/lib/tor/garlic_services/{service_id}'
        self.status = 'initializing'
        self.connections = []
        self.message_count = 0
        
    def setup_onion_service(self):
        """Setup base Tor onion service"""
        try:
            # Create hidden service directory
            os.makedirs(self.hidden_service_dir, mode=0o700, exist_ok=True)
            
            # Configure Tor hidden service
            tor_config = f'''
HiddenServiceDir {self.hidden_service_dir}
HiddenServicePort 80 127.0.0.1:{self.service_port}
HiddenServiceVersion 3
'''
            
            # Backup original torrc
            subprocess.run(['cp', '/etc/tor/torrc', '/etc/tor/torrc.backup'], check=False)
            
            # Add our service
            with open('/etc/tor/torrc', 'a') as f:
                f.write(tor_config)
            
            # Reload Tor configuration
            subprocess.run(['systemctl', 'reload', 'tor'], check=False)
            
            # Wait for onion address to be generated
            time.sleep(10)
            
            hostname_file = os.path.join(self.hidden_service_dir, 'hostname')
            if os.path.exists(hostname_file):
                with open(hostname_file, 'r') as f:
                    self.onion_address = f.read().strip()
                
                # Generate garlic address (onion + clove signature)
                clove_signature = self.clove_security.create_clove_signature(
                    self.onion_address.encode(), self.service_id
                )
                self.garlic_address = f'{self.onion_address}.garlic{base64.b16encode(clove_signature).decode()[:8].lower()}'
                
                self.status = 'active'
                return True
            
        except Exception as e:
            print(f"Failed to setup onion service: {e}")
            self.status = 'failed'
            return False
    
    def handle_connection(self, client_socket):
        """Handle incoming connection with clove security"""
        try:
            # Receive encrypted data
            encrypted_data = client_socket.recv(4096)
            
            if not encrypted_data:
                return
            
            # Extract clove ID and decrypt
            clove_id = encrypted_data[:16].hex()
            payload = encrypted_data[16:]
            
            # Verify clove signature
            if len(payload) < 32:
                client_socket.send(b'INVALID_DATA')
                return
                
            signature = payload[-32:]
            message = payload[:-32]
            
            if not self.clove_security.verify_clove_signature(message, signature, clove_id):
                client_socket.send(b'INVALID_SIGNATURE')
                return
            
            # Decrypt with clove security
            decrypted_data = self.clove_security.decrypt_with_cloves(message, clove_id)
            
            # Process the request
            response = self.process_request(decrypted_data.decode())
            
            # Encrypt response with clove security
            encrypted_response = self.clove_security.encrypt_with_cloves(response.encode(), clove_id)
            response_signature = self.clove_security.create_clove_signature(encrypted_response, clove_id)
            
            # Send encrypted response
            client_socket.send(encrypted_response + response_signature)
            
            self.message_count += 1
            
        except Exception as e:
            print(f"Connection handling error: {e}")
        finally:
            client_socket.close()
    
    def process_request(self, request):
        """Process service request"""
        try:
            data = json.loads(request)
            
            if data.get('type') == 'status':
                return json.dumps({
                    'service_id': self.service_id,
                    'status': self.status,
                    'onion_address': self.onion_address,
                    'garlic_address': self.garlic_address,
                    'message_count': self.message_count,
                    'timestamp': datetime.now(timezone.utc).isoformat()
                })
            
            elif data.get('type') == 'echo':
                return json.dumps({
                    'echo': data.get('message', ''),
                    'service_id': self.service_id,
                    'timestamp': datetime.now(timezone.utc).isoformat()
                })
            
            elif data.get('type') == 'garlic_info':
                return json.dumps({
                    'service_id': self.service_id,
                    'clove_layers': self.clove_security.clove_layers,
                    'crypto_available': CRYPTO_AVAILABLE,
                    'tor_status': 'active',
                    'timestamp': datetime.now(timezone.utc).isoformat()
                })
            
            else:
                return json.dumps({
                    'error': 'Unknown request type',
                    'service_id': self.service_id,
                    'available_types': ['status', 'echo', 'garlic_info']
                })
                
        except Exception as e:
            return json.dumps({'error': str(e)})
    
    def start_service(self):
        """Start the garlic service"""
        if not self.setup_onion_service():
            return False
        
        # Start local service server
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        server_socket.bind(('127.0.0.1', self.service_port))
        server_socket.listen(5)
        
        print(f"🧄 Garlic Service {self.service_id} active")
        print(f"   Onion: {self.onion_address}")
        print(f"   Garlic: {self.garlic_address}")
        print(f"   Local: 127.0.0.1:{self.service_port}")
        print(f"   Crypto: {'✅' if CRYPTO_AVAILABLE else '⚠️ Fallback'}")
        
        while True:
            try:
                client_socket, addr = server_socket.accept()
                client_thread = threading.Thread(
                    target=self.handle_connection,
                    args=(client_socket,)
                )
                client_thread.daemon = True
                client_thread.start()
                
            except KeyboardInterrupt:
                break
            except Exception as e:
                print(f"Service error: {e}")
        
        server_socket.close()

class GarlicServicesManager:
    """Manager for all Garlic Services"""
    
    def __init__(self):
        self.clove_security = CloveSecurity()
        self.services = {}
        self.tor_status = 'unknown'
        
    def setup_tor(self):
        """Setup and configure Tor"""
        try:
            print("🔐 Checking Tor status...")
            
            # Check if Tor is installed and running
            result = subprocess.run(['systemctl', 'is-active', 'tor'], 
                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            
            if result.returncode == 0:
                self.tor_status = 'active'
                print("✅ Tor is active")
                return True
            else:
                print("❌ Tor is not active")
                self.tor_status = 'failed'
                return False
                
        except Exception as e:
            print(f"❌ Tor check failed: {e}")
            self.tor_status = 'failed'
            return False
    
    def create_service(self, service_id, service_port):
        """Create a new garlic service"""
        service = GarlicService(service_id, service_port, self.clove_security)
        self.services[service_id] = service
        return service
    
    def start_all_services(self):
        """Start all garlic services"""
        print(f"\n🧄 STARTING GARLIC SERVICES")
        print(f"🔐 Tor Status: {self.tor_status}")
        print(f"🛡️ Clove Security: {self.clove_security.clove_layers} layers")
        print(f"🔧 Cryptography: {'✅ Available' if CRYPTO_AVAILABLE else '⚠️ Fallback mode'}")
        print("=" * 60)
        
        for service_id, service in self.services.items():
            service_thread = threading.Thread(
                target=service.start_service
            )
            service_thread.daemon = True
            service_thread.start()
            time.sleep(2)  # Stagger service starts
    
    def get_status(self):
        """Get status of all services"""
        return {
            'tor_status': self.tor_status,
            'clove_layers': self.clove_security.clove_layers,
            'crypto_available': CRYPTO_AVAILABLE,
            'services': {
                sid: {
                    'status': svc.status,
                    'onion_address': svc.onion_address,
                    'garlic_address': svc.garlic_address,
                    'message_count': svc.message_count
                }
                for sid, svc in self.services.items()
            },
            'timestamp': datetime.now(timezone.utc).isoformat()
        }

def main():
    """Main Garlic Services launcher"""
    print("🧄 GARLIC SERVICES - Enhanced Tor with Clove Security")
    print("📍 Toronto Server Deployment")
    print(f"🕐 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print("=" * 60)
    
    manager = GarlicServicesManager()
    
    # Setup Tor
    if not manager.setup_tor():
        print("❌ Tor not available - using simulation mode")
    
    # Create services
    services_config = [
        ('pirate-hub', 8080),
        ('harmonic-sync', 8081),
        ('dragon-gateway', 8082),
        ('clove-auth', 8083),
        ('garlic-chat', 8084)
    ]
    
    for service_id, port in services_config:
        manager.create_service(service_id, port)
    
    # Start all services
    manager.start_all_services()
    
    print("\n🎉 ALL GARLIC SERVICES ACTIVE")
    print("🧄 Enhanced Tor connectivity with Clove security")
    print("🛡️ Multi-layer cryptographic protection")
    print("=" * 60)
    
    try:
        # Keep main thread alive
        while True:
            time.sleep(60)
            status = manager.get_status()
            active_services = len([s for s in status['services'].values() if s['status'] == 'active'])
            print(f"\n📊 Status Update: {active_services}/{len(status['services'])} services active")
            
    except KeyboardInterrupt:
        print("\n🛑 Shutting down Garlic Services...")
    
    return 0

if __name__ == '__main__':
    exit(main())
