"""
{{ project.name }} - Microservice
Built with Gobstopper Framework
"""

import asyncio
import json
from datetime import datetime
from typing import Optional, Dict, Any
from gobstopper import Gobstopper
from gobstopper.middleware.cors import CORSMiddleware
from gobstopper.middleware.security import SecurityMiddleware
from gobstopper.tasks.queue import TaskPriority
from gobstopper.utils.rate_limiter import RateLimiter

# Import project modules
from services import business_logic, external_apis, data_access
from models import schemas, validators
from utils import auth, metrics, logging
from tasks import data_sync, notifications, cleanup
from config import settings

# Initialize Gobstopper application
app = Gobstopper(__name__)

# Rate limiter for API endpoints
rate_limiter = RateLimiter(
    max_requests=settings.RATE_LIMIT_REQUESTS,
    window_seconds=settings.RATE_LIMIT_WINDOW
)

# Configure middleware
app.add_middleware(SecurityMiddleware(
    secret_key=settings.SECRET_KEY,
    csrf_protection=True
), priority=100)

app.add_middleware(CORSMiddleware(
    origins=settings.ALLOWED_ORIGINS,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH"],
    allow_headers=["*"]
), priority=95)

# Context processors
@app.context_processor
def inject_service_info():
    return {
        "service_name": "{{ project.name }}",
        "version": settings.SERVICE_VERSION,
        "environment": settings.ENVIRONMENT,
        "api_base_url": settings.API_BASE_URL
    }

# Background tasks
@app.task("sync_data", category="sync")
async def sync_data_task(source: str, target: str):
    """Background task for data synchronization"""
    return await data_sync.sync_between_services(source, target)

@app.task("send_notification", category="notifications")
async def send_notification_task(recipient: str, message: dict, channel: str = "email"):
    """Send notifications to external services"""
    return await notifications.send_notification(recipient, message, channel)

@app.task("cleanup_data", category="maintenance")
async def cleanup_data_task(older_than_days: int = 30):
    """Clean up old data"""
    return await cleanup.remove_old_data(older_than_days)

@app.task("process_webhook", category="webhooks")
async def process_webhook_task(webhook_data: dict, source: str):
    """Process incoming webhook data"""
    return await external_apis.process_webhook(webhook_data, source)

# Health and metrics endpoints
@app.get("/health")
async def health_check(request):
    """Comprehensive health check"""
    health_status = {
        "status": "healthy",
        "service": "{{ project.name }}",
        "version": settings.SERVICE_VERSION,
        "timestamp": datetime.utcnow().isoformat(),
        "checks": {}
    }
    
    # Database connectivity
    try:
        await data_access.check_database_connection()
        health_status["checks"]["database"] = "healthy"
    except Exception as e:
        health_status["checks"]["database"] = f"unhealthy: {str(e)}"
        health_status["status"] = "degraded"
    
    # External service connectivity
    try:
        await external_apis.check_external_services()
        health_status["checks"]["external_apis"] = "healthy"
    except Exception as e:
        health_status["checks"]["external_apis"] = f"unhealthy: {str(e)}"
        health_status["status"] = "degraded"
    
    # Background task queue
    try:
        queue_info = await app.task_queue.get_queue_info()
        health_status["checks"]["task_queue"] = {
            "status": "healthy",
            "pending_tasks": queue_info.get("pending", 0),
            "active_workers": queue_info.get("workers", 0)
        }
    except Exception as e:
        health_status["checks"]["task_queue"] = f"unhealthy: {str(e)}"
        health_status["status"] = "degraded"
    
    status_code = 200 if health_status["status"] == "healthy" else 503
    return health_status, status_code

@app.get("/metrics")
async def get_metrics(request):
    """Prometheus-compatible metrics endpoint"""
    return await metrics.get_prometheus_metrics()

@app.get("/info")
async def service_info(request):
    """Service information and configuration"""
    return {
        "service": "{{ project.name }}",
        "version": settings.SERVICE_VERSION,
        "environment": settings.ENVIRONMENT,
        "build_time": settings.BUILD_TIME,
        "git_commit": settings.GIT_COMMIT,
        "uptime": await metrics.get_uptime(),
        "features": settings.ENABLED_FEATURES,
        "dependencies": {
            "database": settings.DATABASE_TYPE,
            "cache": settings.CACHE_TYPE,
            "message_queue": settings.MESSAGE_QUEUE_TYPE
        }
    }

# Core business logic endpoints
@app.get("/api/v1/{{ project.python_module }}")
async def list_resources(request):
    """List {{ project.python_module }} resources"""
    
    # Rate limiting
    client_id = request.client_host
    if not await rate_limiter.allow_request(client_id):
        return {"error": "Rate limit exceeded"}, 429
    
    # Authentication (if enabled)
    if settings.REQUIRE_AUTH:
        user = await auth.get_current_user(request)
        if not user:
            return {"error": "Authentication required"}, 401
    
    # Query parameters
    page = int(request.args.get("page", [1])[0])
    limit = min(int(request.args.get("limit", [50])[0]), 100)  # Max 100 items
    filters = {k: v[0] for k, v in request.args.items() if k not in ["page", "limit"]}
    
    # Get resources
    result = await business_logic.list_resources(
        page=page,
        limit=limit,
        filters=filters
    )
    
    # Track metrics
    await metrics.track_api_request("/api/v1/{{ project.python_module }}", "GET", 200)
    
    return result

@app.get("/api/v1/{{ project.python_module }}/<resource_id>")
async def get_resource(request, resource_id: str):
    """Get specific {{ project.python_module }} resource"""
    
    # Rate limiting
    client_id = request.client_host
    if not await rate_limiter.allow_request(client_id):
        return {"error": "Rate limit exceeded"}, 429
    
    # Authentication (if enabled)
    if settings.REQUIRE_AUTH:
        user = await auth.get_current_user(request)
        if not user:
            return {"error": "Authentication required"}, 401
    
    # Get resource
    resource = await business_logic.get_resource_by_id(resource_id)
    if not resource:
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "GET", 404)
        return {"error": "Resource not found"}, 404
    
    # Authorization check
    if settings.REQUIRE_AUTH and not await auth.can_access_resource(user, resource):
        return {"error": "Access denied"}, 403
    
    await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "GET", 200)
    return resource.to_dict()

@app.post("/api/v1/{{ project.python_module }}")
async def create_resource(request):
    """Create new {{ project.python_module }} resource"""
    
    # Rate limiting
    client_id = request.client_host
    if not await rate_limiter.allow_request(client_id):
        return {"error": "Rate limit exceeded"}, 429
    
    # Authentication required for write operations
    user = await auth.get_current_user(request)
    if not user:
        return {"error": "Authentication required"}, 401
    
    # Parse and validate request data
    try:
        data = await request.get_json()
    except Exception:
        return {"error": "Invalid JSON"}, 400
    
    validation_result = validators.validate_create_request(data)
    if not validation_result.is_valid:
        return {"error": "Validation failed", "details": validation_result.errors}, 400
    
    # Authorization check
    if not await auth.can_create_resource(user):
        return {"error": "Insufficient permissions"}, 403
    
    try:
        # Create resource
        resource = await business_logic.create_resource(data, user_id=user.id)
        
        # Queue background tasks
        if settings.ENABLE_NOTIFICATIONS:
            await app.add_background_task(
                "send_notification",
                category="notifications",
                recipient=user.email,
                message={"type": "resource_created", "resource_id": resource.id},
                channel="email"
            )
        
        if settings.ENABLE_SYNC:
            await app.add_background_task(
                "sync_data",
                category="sync",
                source="local",
                target="external"
            )
        
        await metrics.track_api_request("/api/v1/{{ project.python_module }}", "POST", 201)
        
        return resource.to_dict(), 201
        
    except ValueError as e:
        await metrics.track_api_request("/api/v1/{{ project.python_module }}", "POST", 400)
        return {"error": str(e)}, 400
    except Exception as e:
        app.logger.error(f"Error creating resource: {e}")
        await metrics.track_api_request("/api/v1/{{ project.python_module }}", "POST", 500)
        return {"error": "Internal server error"}, 500

@app.put("/api/v1/{{ project.python_module }}/<resource_id>")
async def update_resource(request, resource_id: str):
    """Update {{ project.python_module }} resource"""
    
    # Rate limiting
    client_id = request.client_host
    if not await rate_limiter.allow_request(client_id):
        return {"error": "Rate limit exceeded"}, 429
    
    # Authentication required for write operations
    user = await auth.get_current_user(request)
    if not user:
        return {"error": "Authentication required"}, 401
    
    # Check if resource exists
    resource = await business_logic.get_resource_by_id(resource_id)
    if not resource:
        return {"error": "Resource not found"}, 404
    
    # Authorization check
    if not await auth.can_update_resource(user, resource):
        return {"error": "Insufficient permissions"}, 403
    
    # Parse and validate request data
    try:
        data = await request.get_json()
    except Exception:
        return {"error": "Invalid JSON"}, 400
    
    validation_result = validators.validate_update_request(data)
    if not validation_result.is_valid:
        return {"error": "Validation failed", "details": validation_result.errors}, 400
    
    try:
        # Update resource
        updated_resource = await business_logic.update_resource(resource_id, data, user_id=user.id)
        
        # Queue sync task
        if settings.ENABLE_SYNC:
            await app.add_background_task(
                "sync_data",
                category="sync",
                source="local",
                target="external"
            )
        
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "PUT", 200)
        
        return updated_resource.to_dict()
        
    except ValueError as e:
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "PUT", 400)
        return {"error": str(e)}, 400
    except Exception as e:
        app.logger.error(f"Error updating resource {resource_id}: {e}")
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "PUT", 500)
        return {"error": "Internal server error"}, 500

@app.delete("/api/v1/{{ project.python_module }}/<resource_id>")
async def delete_resource(request, resource_id: str):
    """Delete {{ project.python_module }} resource"""
    
    # Rate limiting
    client_id = request.client_host
    if not await rate_limiter.allow_request(client_id):
        return {"error": "Rate limit exceeded"}, 429
    
    # Authentication required for delete operations
    user = await auth.get_current_user(request)
    if not user:
        return {"error": "Authentication required"}, 401
    
    # Check if resource exists
    resource = await business_logic.get_resource_by_id(resource_id)
    if not resource:
        return {"error": "Resource not found"}, 404
    
    # Authorization check
    if not await auth.can_delete_resource(user, resource):
        return {"error": "Insufficient permissions"}, 403
    
    try:
        # Delete resource
        await business_logic.delete_resource(resource_id, user_id=user.id)
        
        # Queue cleanup and sync tasks
        if settings.ENABLE_SYNC:
            await app.add_background_task(
                "sync_data",
                category="sync",
                source="local",
                target="external"
            )
        
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "DELETE", 204)
        
        return "", 204
        
    except Exception as e:
        app.logger.error(f"Error deleting resource {resource_id}: {e}")
        await metrics.track_api_request("/api/v1/{{ project.python_module }}/{id}", "DELETE", 500)
        return {"error": "Internal server error"}, 500

# Batch operations
@app.post("/api/v1/{{ project.python_module }}/batch")
async def batch_operation(request):
    """Perform batch operations on resources"""
    
    # Authentication required
    user = await auth.get_current_user(request)
    if not user:
        return {"error": "Authentication required"}, 401
    
    try:
        data = await request.get_json()
        operation = data.get("operation")
        resource_ids = data.get("resource_ids", [])
        
        if not operation or not resource_ids:
            return {"error": "Operation and resource_ids required"}, 400
        
        # Process batch operation
        result = await business_logic.batch_operation(operation, resource_ids, user_id=user.id)
        
        return {
            "operation": operation,
            "processed": result["processed"],
            "failed": result["failed"],
            "results": result["results"]
        }
        
    except Exception as e:
        app.logger.error(f"Batch operation error: {e}")
        return {"error": "Internal server error"}, 500

# Webhook endpoints
@app.post("/webhooks/<source>")
async def webhook_handler(request, source: str):
    """Handle incoming webhooks from external services"""
    
    # Validate webhook source
    if source not in settings.ALLOWED_WEBHOOK_SOURCES:
        return {"error": "Unknown webhook source"}, 400
    
    # Verify webhook signature (if configured)
    if settings.VERIFY_WEBHOOK_SIGNATURES:
        signature = request.headers.get("X-Webhook-Signature")
        if not await auth.verify_webhook_signature(await request.get_body(), signature, source):
            return {"error": "Invalid signature"}, 401
    
    try:
        webhook_data = await request.get_json()
        
        # Queue webhook processing
        task_id = await app.add_background_task(
            "process_webhook",
            category="webhooks",
            priority=TaskPriority.HIGH,
            webhook_data=webhook_data,
            source=source
        )
        
        return {
            "status": "received",
            "task_id": task_id,
            "source": source
        }
        
    except Exception as e:
        app.logger.error(f"Webhook processing error: {e}")
        return {"error": "Webhook processing failed"}, 500

# Admin endpoints
@app.get("/admin/stats")
async def admin_stats(request):
    """Administrative statistics"""
    user = await auth.get_current_user(request)
    if not user or not user.is_admin:
        return {"error": "Admin access required"}, 403
    
    stats = await metrics.get_detailed_stats()
    return stats

@app.post("/admin/maintenance")
async def trigger_maintenance(request):
    """Trigger maintenance tasks"""
    user = await auth.get_current_user(request)
    if not user or not user.is_admin:
        return {"error": "Admin access required"}, 403
    
    data = await request.get_json()
    maintenance_type = data.get("type", "cleanup")
    
    if maintenance_type == "cleanup":
        task_id = await app.add_background_task(
            "cleanup_data",
            category="maintenance",
            older_than_days=data.get("older_than_days", 30)
        )
    else:
        return {"error": "Unknown maintenance type"}, 400
    
    return {"task_id": task_id, "status": "queued"}

# Startup tasks
@app.on_startup
async def startup():
    """Initialize service on startup"""
    # Initialize database connections
    await data_access.initialize_connections()
    
    # Initialize external API clients
    await external_apis.initialize_clients()
    
    # Start background workers
    await app.start_task_workers("sync", worker_count=2)
    await app.start_task_workers("notifications", worker_count=1)
    await app.start_task_workers("webhooks", worker_count=3)
    await app.start_task_workers("maintenance", worker_count=1)
    
    # Schedule periodic tasks
    asyncio.create_task(schedule_periodic_tasks())
    
    # Initialize metrics
    await metrics.initialize()
    
    app.logger.info(f"🚀 {{ project.name }} microservice started")
    app.logger.info(f"Version: {settings.SERVICE_VERSION}")
    app.logger.info(f"Environment: {settings.ENVIRONMENT}")

async def schedule_periodic_tasks():
    """Schedule periodic maintenance tasks"""
    while True:
        # Sync data every 5 minutes
        if datetime.now().minute % 5 == 0:
            if settings.ENABLE_SYNC:
                await app.add_background_task(
                    "sync_data",
                    category="sync",
                    source="local",
                    target="external"
                )
        
        # Cleanup old data daily at 3 AM
        if datetime.now().hour == 3 and datetime.now().minute == 0:
            await app.add_background_task(
                "cleanup_data",
                category="maintenance",
                older_than_days=settings.DATA_RETENTION_DAYS
            )
        
        await asyncio.sleep(60)  # Check every minute

# Shutdown tasks
@app.on_shutdown
async def shutdown():
    """Cleanup on shutdown"""
    await data_access.close_connections()
    await external_apis.close_clients()
    await metrics.close()
    await app.task_queue.shutdown()
    app.logger.info("👋 {{ project.name }} microservice shutting down")

if __name__ == "__main__":
    print("=" * 50)
    print("{{ project.name }} - Microservice")
    print("=" * 50)
    print("Run with: granian --interface rsgi --reload app:app")
    print(f"Health Check: http://localhost:8000/health")
    print(f"Metrics: http://localhost:8000/metrics")
    print(f"API Base: http://localhost:8000/api/v1")