# createsonline/app.py
"""
CREATESONLINE Application Core

Main application class and request handling
"""

import sys
import json
import hashlib
import logging
from datetime import datetime
from typing import Dict, Any, Optional, List, Callable, Union

# System requirements check
if sys.version_info < (3, 9):
    raise RuntimeError(f"CREATESONLINE requires Python 3.9+. Current: {sys.version}")

# Setup logging
logger = logging.getLogger("createsonline")

# Get version
from . import __version__


class CreatesonlineInternalApp:
    """
    Pure CREATESONLINE core application - ZERO external dependencies
    
    This is the internal implementation that works with just Python stdlib.
    Provides full AI-native framework capabilities without any external packages.
    """
    
    def __init__(
        self,
        title: str = "CREATESONLINE Application",
        description: str = "AI-powered application built with CREATESONLINE",
        version: str = "1.0.0",
        ai_config: Optional[Dict[str, Any]] = None,
        debug: bool = False,
        **kwargs
    ):
        # Core application metadata
        self.title = title
        self.description = description
        self.version = version
        self.debug = debug
        self.ai_config = ai_config or {}
        
        # Internal routing system
        self.routes = {}
        self.middleware = []
        self.startup_handlers = []
        self.shutdown_handlers = []
        
        # AI-native features (internal implementations)
        self.ai_enabled = True
        self.ai_features = []
        self.ai_services = CreatesonlineInternalAI()
        
        # Setup framework routes
        self._setup_framework_routes()
        
        logger.info(f"CREATESONLINE v{__version__} initialized (Internal Core)")
        logger.info(f"✨ {self.title} - AI-Native Framework Ready")
    
    def _setup_framework_routes(self):
        """Setup built-in CREATESONLINE framework routes"""
        
        @self.get("/health")
        async def health_check(request):
            return {
                "status": "healthy",
                "framework": "CREATESONLINE",
                "version": __version__,
                "timestamp": datetime.utcnow().isoformat()
            }
    
    # ========================================
    # PURE ASGI IMPLEMENTATION
    # ========================================
    
    async def __call__(self, scope, receive, send):
        """Pure ASGI interface - no external dependencies"""
        
        if scope['type'] == 'http':
            await self._handle_http(scope, receive, send)
        elif scope['type'] == 'websocket':
            await self._handle_websocket(scope, receive, send)
    
    async def _handle_http(self, scope, receive, send):
        """Handle HTTP requests with internal router"""
        
        path = scope['path']
        method = scope['method']
        route_key = f"{method}:{path}"
        
        # Create internal request object
        request = CreatesonlineInternalRequest(scope, receive)
        
        try:
            # Check for static files first (before routing)
            if method == 'GET' and self._is_static_path(path):
                await self._serve_static_file(path, send)
                return
            
            # Find matching route
            if route_key in self.routes:
                handler = self.routes[route_key]
                response_data = await handler(request)
                status = 200
            else:
                # 404 handler
                response_data = {
                    "error": "Not Found",
                    "path": path,
                    "method": method,
                    "framework": "CREATESONLINE",
                    "available_routes": list(self.routes.keys())
                }
                status = 404
            
            # Send response
            if isinstance(response_data, dict):
                await self._send_json_response(send, response_data, status=status)
            else:
                await self._send_text_response(send, str(response_data))
                
        except Exception as e:
            # Error handling - log the full traceback
            logger.exception(f"Request handling error for {method} {path}")
            error_data = {
                "error": "Internal Server Error",
                "message": str(e) if self.debug else "Something went wrong",
                "framework": "CREATESONLINE"
            }
            await self._send_json_response(send, error_data, status=500)
    
    async def _handle_websocket(self, scope, receive, send):
        """Handle WebSocket connections with security measures"""
        await send({'type': 'websocket.accept'})
        
        message_count = 0
        max_messages = 100
        
        while True:
            message = await receive()
            if message['type'] == 'websocket.disconnect':
                break
            elif message['type'] == 'websocket.receive':
                message_count += 1
                if message_count > max_messages:
                    logger.warning("WebSocket rate limit exceeded")
                    await send({'type': 'websocket.close', 'code': 1008})
                    break
                
                user_text = message.get('text', '')
                if user_text:
                    logger.info(f"WebSocket message received (length: {len(user_text)})")
                
                await send({
                    'type': 'websocket.send',
                    'text': json.dumps({
                        "framework": "CREATESONLINE",
                        "message": "WebSocket message processed",
                        "message_count": message_count
                    })
                })
    
    async def _send_json_response(self, send, data, status=200):
        """Send JSON response"""
        response_body = json.dumps(data, indent=2 if self.debug else None).encode('utf-8')
        
        await send({
            'type': 'http.response.start',
            'status': status,
            'headers': [
                [b'content-type', b'application/json'],
                [b'content-length', str(len(response_body)).encode()],
                [b'x-framework', b'CREATESONLINE'],
                [b'x-version', __version__.encode()],
            ],
        })
        
        await send({
            'type': 'http.response.body',
            'body': response_body,
        })
    
    async def _send_text_response(self, send, text, status=200):
        """Send plain text or HTML response"""
        response_body = text.encode('utf-8')
        
        # Detect if it's HTML
        content_type = b'text/html; charset=utf-8' if text.strip().startswith('<!DOCTYPE') or text.strip().startswith('<html') else b'text/plain'
        
        await send({
            'type': 'http.response.start',
            'status': status,
            'headers': [
                [b'content-type', content_type],
                [b'content-length', str(len(response_body)).encode()],
                [b'x-framework', b'CREATESONLINE'],
            ],
        })
        
        await send({
            'type': 'http.response.body',
            'body': response_body,
        })
    
    def _is_static_path(self, path: str) -> bool:
        """Check if path is a static file request"""
        static_prefixes = ['/static/', '/css/', '/js/', '/images/', '/img/', '/assets/', '/media/', '/icons/']
        static_extensions = {'.css', '.js', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.webp', 
                           '.woff', '.woff2', '.ttf', '.eot', '.pdf', '.txt', '.json', '.xml'}
        
        for prefix in static_prefixes:
            if path.startswith(prefix):
                return True
        
        for ext in static_extensions:
            if path.endswith(ext):
                return True
        
        return False
    
    async def _serve_static_file(self, path, send):
        """Serve static files using StaticFileHandler"""
        try:
            from .static_files import static_handler
            
            content, status_code, headers = static_handler.serve_file(path)
            
            header_list = [[k.encode() if isinstance(k, str) else k, 
                           v.encode() if isinstance(v, str) else v] 
                          for k, v in headers.items()]
            
            header_list.append([b'x-framework', b'CREATESONLINE'])
            
            await send({
                'type': 'http.response.start',
                'status': status_code,
                'headers': header_list,
            })
            
            await send({
                'type': 'http.response.body',
                'body': content if isinstance(content, bytes) else content.encode('utf-8'),
            })
            
        except Exception as e:
            logger.error(f"Error serving static file {path}: {e}")
            await self._send_json_response(send, {"error": "Static file error", "message": str(e)}, status=500)
    
    # ========================================
    # ROUTING DECORATORS
    # ========================================
    
    def get(self, path: str):
        """GET route decorator"""
        def decorator(func):
            self.routes[f"GET:{path}"] = func
            return func
        return decorator
    
    def post(self, path: str):
        """POST route decorator"""
        def decorator(func):
            self.routes[f"POST:{path}"] = func
            return func
        return decorator
    
    def put(self, path: str):
        """PUT route decorator"""
        def decorator(func):
            self.routes[f"PUT:{path}"] = func
            return func
        return decorator
    
    def delete(self, path: str):
        """DELETE route decorator"""
        def decorator(func):
            self.routes[f"DELETE:{path}"] = func
            return func
        return decorator
    
    def route(self, path: str, methods: List[str] = None):
        """Multi-method route decorator"""
        if methods is None:
            methods = ["GET"]
        
        def decorator(func):
            for method in methods:
                self.routes[f"{method.upper()}:{path}"] = func
            return func
        return decorator
    
    # ========================================
    # AI-NATIVE FEATURES
    # ========================================
    
    def enable_ai_features(self, features: List[str]):
        """Enable AI features"""
        for feature in features:
            if feature not in self.ai_features:
                self.ai_features.append(feature)
                logger.info(f"AI Feature enabled: {feature}")
        return self
    
    # ========================================
    # LIFECYCLE EVENTS
    # ========================================
    
    def on_startup(self, func: Callable):
        """Register startup handler"""
        self.startup_handlers.append(func)
        return func
    
    def on_shutdown(self, func: Callable):
        """Register shutdown handler"""
        self.shutdown_handlers.append(func)
        return func
    
    # ========================================
    # SERVER RUNNER
    # ========================================
    
    def run(self, host: str = "127.0.0.1", port: int = 8000, reload: bool = False):
        """Run the application"""
        from .server import run_server
        
        logger.info("Starting CREATESONLINE Pure Python Server")
        logger.info(f"Framework: CREATESONLINE v{__version__}")
        run_server(self, host=host, port=port, reload=reload)


class CreatesonlineInternalRequest:
    """Internal request object"""
    
    def __init__(self, scope, receive):
        self.scope = scope
        self.receive = receive
        self.path = scope['path']
        self.method = scope['method']
        self.headers = dict(scope.get('headers', []))
        self.query_params = self._parse_query_string(scope.get('query_string', b''))
        self.path_params = scope.get('path_params', {})
    
    def _parse_query_string(self, query_string):
        """Parse query string into dict"""
        if not query_string:
            return {}
        
        params = {}
        for pair in query_string.decode().split('&'):
            if '=' in pair:
                key, value = pair.split('=', 1)
                params[key] = value
        return params
    
    async def json(self):
        """Parse JSON body"""
        body = await self._get_body()
        return json.loads(body.decode())
    
    async def _get_body(self):
        """Get request body"""
        body = b''
        while True:
            message = await self.receive()
            if message['type'] == 'http.request':
                body += message.get('body', b'')
                if not message.get('more_body', False):
                    break
        return body


class CreatesonlineInternalAI:
    """Internal AI services"""
    
    def __init__(self):
        self.cache = {}
    
    async def generate_text(self, prompt: str, **kwargs) -> str:
        """Generate text using internal algorithms"""
        templates = {
            "hello": "Hello! Welcome to CREATESONLINE - the AI-Native framework.",
            "describe": f"CREATESONLINE is an innovative AI-native web framework.",
        }
        
        prompt_lower = prompt.lower()
        for key, template in templates.items():
            if key in prompt_lower:
                return template
        
        return f"CREATESONLINE AI Response: Generated content for '{prompt[:50]}...'"
    
    def get_embedding(self, text: str, dimensions: int = 128) -> List[float]:
        """Generate hash-based embeddings"""
        hash_obj = hashlib.md5(text.encode())
        hash_bytes = hash_obj.digest()
        
        embedding = []
        for i in range(dimensions):
            byte_index = i % len(hash_bytes)
            value = (hash_bytes[byte_index] / 255.0) - 0.5
            embedding.append(value)
        
        return embedding


# ========================================
# MAIN FRAMEWORK API
# ========================================

def create_app(
    title: str = "CREATESONLINE Application",
    description: str = "AI-powered application built with CREATESONLINE",
    version: str = "1.0.0",
    ai_config: Optional[Dict[str, Any]] = None,
    debug: bool = False,
    **kwargs
) -> CreatesonlineInternalApp:
    """Create a CREATESONLINE application instance"""
    
    # TRY: Use full-featured app if available
    try:
        from createsonline.config.app import CreatesonlineApp
        logger.info("🚀 Loading full-featured CREATESONLINE...")
        return CreatesonlineApp(
            title=title,
            description=description,
            version=version,
            ai_config=ai_config or {},
            debug=debug,
            **kwargs
        )
    except ImportError:
        # FALLBACK: Use internal core
        logger.info("✅ Using CREATESONLINE Internal Core")
        return CreatesonlineInternalApp(
            title=title,
            description=description,
            version=version,
            ai_config=ai_config or {},
            debug=debug,
            **kwargs
        )
