"""FastAPI application module.

Generates a Docker Compose service for a FastAPI application
with uvicorn workers, health checks, and proper env var wiring.
"""

from __future__ import annotations

import json
import logging
import time
from typing import Any

from nikame.modules.base import BaseModule, ModuleContext


class FastAPIModule(BaseModule):
    """FastAPI web framework module.

    Generates a containerized FastAPI service with:
    - Auto-configured uvicorn workers
    - Health check endpoint
    - CORS middleware configuration
    - Environment variable wiring to all backend services
    """

    NAME = "fastapi"
    CATEGORY = "api"
    DESCRIPTION = "FastAPI web framework with uvicorn ASGI server"
    DEFAULT_VERSION = "0.109"
    DEPENDENCIES: list[str] = []
    CONFLICTS: list[str] = []

    def __init__(self, config: dict[str, Any], ctx: ModuleContext) -> None:
        super().__init__(config, ctx)
        workers_val = config.get("workers", "auto")
        self.workers: int = 4 if workers_val == "auto" else int(workers_val)
        self.cors_origins: list[str] = config.get("cors_origins", ["http://localhost:3000"])
        self.port: int = config.get("port", 8000)
        self.rate_limiting: dict[str, Any] = config.get("rate_limiting", {"enabled": False})

    def required_ports(self) -> dict[str, int]:
        """Requested API port."""
        return {"fastapi": self.port}

    def compose_spec(self) -> dict[str, Any]:
        """Generate Docker Compose service spec for FastAPI."""
        spec = {
            "api": {
                "build": {
                    "context": "../app",
                    "dockerfile": "Dockerfile",
                },
                "restart": "unless-stopped",
                "ports": [f"{self.ctx.host_port_map.get('fastapi', self.port)}:{self.port}"],
                "environment": {
                    "APP_NAME": self.ctx.project_name,
                    "APP_ENV": self.ctx.environment,
                    "UVICORN_WORKERS": str(self.workers),
                    "UVICORN_PORT": str(self.port),
                    "CORS_ORIGINS": ",".join(self.cors_origins),
                    **self.ctx.all_env_vars,
                },
                "healthcheck": self.health_check(),
                "networks": [
                    f"{self.ctx.project_name}_network",
                ],
                "labels": {
                    "nikame.module": "fastapi",
                    "nikame.category": "api",
                },
            }
        }

        # Enable hot-reload for local development
        if self.ctx.environment == "local":
            spec["api"]["volumes"] = ["../app:/app"]
            spec["api"]["command"] = [
                "uvicorn", "main:app", 
                "--host", "0.0.0.0", 
                "--port", str(self.port), 
                "--reload"
            ]

        return spec

    def k8s_manifests(self) -> list[dict[str, Any]]:
        """Generate full production-ready K8s architecture for FastAPI."""
        name = "api"
        image = f"{self.ctx.project_name}-{name}:latest"

        manifests: list[dict[str, Any]] = []

        # 1. Service Account
        manifests.append(self.service_account(name))

        # 2. ConfigMap
        manifests.append(self.config_map(name, {"APP_ENV": self.ctx.environment, "PORT": str(self.port)}))

        # 3. Deployment
        deployment: dict[str, Any] = {
            "apiVersion": "apps/v1",
            "kind": "Deployment",
            "metadata": {
                "name": name,
                "namespace": self.ctx.namespace,
                "labels": {"app": name, "nikame.module": self.NAME},
            },
            "spec": {
                "replicas": 1,
                "selector": {"matchLabels": {"app": name}},
                "template": {
                    "metadata": {"labels": {"app": name}},
                    "spec": {
                        "serviceAccountName": name,
                        "initContainers": [
                            self.init_container_wait("postgres", 5432)
                        ],
                        "containers": [
                            {
                                "name": name,
                                "image": f"{self.ctx.project_name}-api:latest",
                                "ports": [{"containerPort": 8000}],
                                "envFrom": [{"configMapRef": {"name": name}}],
                                "resources": self.resource_requirements(),
                                "livenessProbe": {
                                    "httpGet": {"path": "/health", "port": self.port},
                                    "initialDelaySeconds": 10,
                                    "periodSeconds": 30,
                                },
                                "readinessProbe": {
                                    "httpGet": {"path": "/health", "port": self.port},
                                    "initialDelaySeconds": 5,
                                    "periodSeconds": 10,
                                },
                            }
                        ]
                    },
                },
            },
        }

        # 2. Service
        service: dict[str, Any] = {
            "apiVersion": "v1",
            "kind": "Service",
            "metadata": {
                "name": name,
                "namespace": self.ctx.namespace,
                "labels": {"app": name}
            },
            "spec": {
                "selector": {"app": name},
                "ports": [{"port": 80, "targetPort": self.port}],
                "type": "ClusterIP",
            },
        }

        # 3. Production Manifests
        manifests = [
            self.service_account(name),
            self.config_map(f"{name}-config", {"APP_ENV": self.ctx.environment, "PORT": str(self.port)}),
            deployment,
            service,
            self.network_policy(name, allow_from=["ingress-nginx", "traefik"]), # Basic whitelist
            self.hpa(name, min_reps=2, max_reps=10),
            self.pdb(name),
        ]

        if self.ctx.domain:
            manifests.append(self.ingress(name, self.ctx.domain, service_port=80, tls_secret=f"{name}-tls"))

        return manifests

    def health_check(self) -> dict[str, Any]:
        """Docker Compose health check — hits /health endpoint."""
        return {
            "test": ["CMD", "curl", "-f", f"http://localhost:{self.port}/health"],
            "interval": "15s",
            "timeout": "5s",
            "retries": 3,
            "start_period": "10s",
        }

    def env_vars(self) -> dict[str, str]:
        """Expose API URL for other services."""
        return {
            "API_URL": f"http://api:{self.port}",
        }

    def guide_metadata(self) -> dict[str, Any]:
        """FastAPI-specific guide metadata."""
        port = self.ctx.host_port_map.get("fastapi", self.port)
        return {
            "overview": self.DESCRIPTION,
            "urls": [
                {
                    "label": "API (Swagger)",
                    "url": f"http://localhost:{port}/docs",
                    "usage": "Interactive API documentation",
                    "creds": "None"
                },
                {
                    "label": "API (Redoc)",
                    "url": f"http://localhost:{port}/redoc",
                    "usage": "Alternative API documentation",
                    "creds": "None"
                }
            ],
            "api_examples": [
                {
                    "name": "Health Check",
                    "desc": "Check if the API and its dependencies are healthy",
                    "curl": f"curl -X GET http://localhost:{port}/health/ready"
                }
            ],
            "troubleshooting": [
                {
                    "issue": "API not reachable",
                    "fix": f"Check if port {port} is occupied by another process or if the container is running."
                }
            ],
        }

    def prometheus_rules(self) -> list[dict[str, Any]]:
        """Prometheus alert rules for the FastAPI service."""
        return [
            {
                "alert": "APIHighLatency",
                "expr": 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{service="api"}[5m])) > 1',
                "for": "5m",
                "labels": {"severity": "warning"},
                "annotations": {
                    "summary": "API p95 latency above 1 second",
                    "description": "FastAPI p95 latency has been above 1s for 5 minutes.",
                },
            },
            {
                "alert": "APIHighErrorRate",
                "expr": 'rate(http_requests_total{service="api",status=~"5.."}[5m]) / rate(http_requests_total{service="api"}[5m]) > 0.05',
                "for": "5m",
                "labels": {"severity": "critical"},
                "annotations": {
                    "summary": "API error rate above 5%",
                    "description": "FastAPI is returning >5% server errors.",
                },
            },
            {
                "alert": "APIDown",
                "expr": 'up{job="api"} == 0',
                "for": "1m",
                "labels": {"severity": "critical"},
                "annotations": {
                    "summary": "FastAPI service is down",
                    "description": "FastAPI has been unreachable for over 1 minute.",
                },
            },
        ]

    def grafana_dashboard(self) -> dict[str, Any] | None:
        """Grafana dashboard for FastAPI metrics."""
        return {
            "title": f"{self.ctx.project_name} — FastAPI",
            "uid": "nikame-fastapi",
            "panels": [
                {
                    "title": "Request Rate",
                    "type": "timeseries",
                    "targets": [{"expr": 'rate(http_requests_total{service="api"}[5m])'}],
                },
                {
                    "title": "p95 Latency",
                    "type": "timeseries",
                    "targets": [
                        {
                            "expr": 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{service="api"}[5m]))'
                        }
                    ],
                },
                {
                    "title": "Error Rate",
                    "type": "stat",
                    "targets": [
                        {
                            "expr": 'rate(http_requests_total{service="api",status=~"5.."}[5m]) / rate(http_requests_total{service="api"}[5m])'
                        }
                    ],
                },
                {
                    "title": "Active Connections",
                    "type": "gauge",
                    "targets": [{"expr": 'uvicorn_active_connections{service="api"}'}],
                },
            ],
        }

    def compute_cost_monthly_usd(self) -> float | None:
        """Estimate monthly compute cost."""
        # Rough ECS Fargate cost: 0.5 vCPU, 1GB RAM per worker
        return 25.0 * self.workers

    def resource_requirements(self) -> dict[str, Any]:
        """K8s resource requests and limits."""
        return {
            "requests": {"cpu": "250m", "memory": "512Mi"},
            "limits": {"cpu": "1000m", "memory": "1Gi"},
        }

    def scaffold_files(self) -> list[tuple[str, str]]:
        """Generate full production-ready FastAPI application scaffolding using Manifest."""
        project = self.ctx.project_name
        has_db = any(m in ["postgres", "timescaledb", "cockroachdb"] for m in self.ctx.active_modules)
        has_cache = any(m in ["redis", "dragonfly"] for m in self.ctx.active_modules)
        has_messaging = any(m in ["redpanda", "kafka", "rabbitmq"] for m in self.ctx.active_modules)
        has_storage = any(m in ["minio", "s3"] for m in self.ctx.active_modules)
        has_search = any(m in ["elasticsearch"] for m in self.ctx.active_modules)
        has_neo4j = any(m in ["neo4j"] for m in self.ctx.active_modules)
        has_clickhouse = any(m in ["clickhouse"] for m in self.ctx.active_modules)
        has_qdrant = any(m in ["qdrant"] for m in self.ctx.active_modules)
        has_vector = has_qdrant # Alias
        has_temporal = any(m in ["temporal"] for m in self.ctx.active_modules)
        has_ngrok = any(m in ["ngrok"] for m in self.ctx.active_modules)
        has_unleash = any(m in ["unleash"] for m in self.ctx.active_modules)
        has_vault = any(m in ["vault"] for m in self.ctx.active_modules)
        has_mlflow = any(m in ["mlflow"] for m in self.ctx.active_modules)
        has_langfuse = any(m in ["langfuse"] for m in self.ctx.active_modules)
        has_arize = any(m in ["arize-phoenix"] for m in self.ctx.active_modules)
        has_evidently = any(m in ["evidently"] for m in self.ctx.active_modules)
        
        has_gptcache = "gptcache" in self.ctx.active_modules
        has_langchain = "langchain" in self.ctx.active_modules
        has_llamaindex = "llamaindex" in self.ctx.active_modules
        has_haystack = "haystack" in self.ctx.active_modules
        has_agents = has_langchain or has_llamaindex or has_haystack
        
        has_mlops_observability = has_langfuse or has_arize or has_evidently
        has_limiter = self.rate_limiting.get("enabled", False)
        has_smtp = "email" in self.ctx.features

        files: list[tuple[str, str]] = []

        # 1. Collect everything from manifest
        manifest = self.ctx.manifest
        if not manifest:
             # Fallback if manifest is missing (though it shouldn't be in V2)
             return []

        # Assemble imports
        imports = {
            "from contextlib import asynccontextmanager",
            "from fastapi import FastAPI, Request",
            "from fastapi.middleware.cors import CORSMiddleware",
            "from fastapi.responses import JSONResponse",
            "from config import settings",
            "import time",
            "import logging",
            "import json",
        }
        
        # Add router imports
        for router in manifest.routers:
            imports.add(f"import {router.module_path}")

        # Assemble health checks
        health_checks_code = []
        for hc in manifest.health_checks:
            health_checks_code.append(f'    # {hc.name}\n    try:\n        if not ({hc.expression}):\n            raise Exception("{hc.name} unhealthy")\n        logger.info("✓ {hc.name} verified")\n    except Exception as e:\n        logger.error(f"✗ {hc.name} failed: {{e}}")')

        # Assemble lifespan hooks (ensure 4-space indentation)
        def indent(text: str, spaces: int = 4) -> str:
            return "\n".join([" " * spaces + line for line in text.split("\n")])

        startup_hooks = [h.code for h in sorted(manifest.lifespan_startup, key=lambda x: x.order)]
        shutdown_hooks = [h.code for h in sorted(manifest.lifespan_shutdown, key=lambda x: x.order)]

        imports_block = "\n".join(sorted(imports))
        startup_block = "\n\n".join([indent(h) for h in startup_hooks])
        shutdown_block = "\n\n".join([indent(h) for h in shutdown_hooks])
        health_block = "\n\n".join(health_checks_code)

        main_py = f'''"""
FastAPI application entry point.
Auto-generated by NIKAME (Manifest-Driven V2)
"""

{imports_block}

# Setup logging
class JSONFormatter(logging.Formatter):
    def format(self, record):
        log_data = {{
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "message": record.getMessage(),
            "module": record.module,
            "service": settings.APP_NAME,
        }}
        if record.exc_info:
            log_data["exception"] = self.formatException(record.exc_info)
        return json.dumps(log_data)

handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logging.basicConfig(level=logging.INFO, handlers=[handler])
logger = logging.getLogger(__name__)

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup logic
    logger.info(f"🚀 Starting {{settings.APP_NAME}} [{{settings.APP_ENV}}]")
    
{health_block}

{startup_block}

    yield
    
    # Shutdown logic
{shutdown_block}
        
    logger.info(f"💤 Shutting down {{settings.APP_NAME}}")

app = FastAPI(
    title=settings.APP_NAME,
    description="Full-stack API generated by NIKAME",
    version="1.0.0",
    docs_url="/docs",
    redoc_url="/redoc",
    lifespan=lifespan,
)

# Middleware for request timing
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
    start_time = time.time()
    response = await call_next(request)
    process_time = time.time() - start_time
    response.headers["X-Process-Time"] = str(process_time)
    return response

# CORS Middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origins_list,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Exception Handler
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    logger.error(f"Global error: {{exc}}", exc_info=True)
    return JSONResponse(
        status_code=500,
        content={{"detail": "Internal Server Error", "type": type(exc).__name__}},
    )

# Register Routers
'''
        for router in manifest.routers:
            main_py += f"app.include_router({router.module_path}.{router.router_var}, prefix='{router.prefix}', tags={json.dumps(router.tags)})\n"
        
        main_py += "\n@app.get('/health')\nasync def health():\n    return {'status': 'healthy'}\n"
        
        main_py += '''
@app.get("/", tags=["root"])
async def root():
    return {
        "message": f"Welcome to {settings.APP_NAME}",
        "env": settings.APP_ENV,
        "docs": "/docs",
        "health": "/health"
    }
'''
        files.append(("main.py", main_py))

        # 2. app/config.py
        config_py = f'''"""
Application configuration using Pydantic Settings.
"""

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    # App
    APP_NAME: str = "{project}"
    APP_ENV: str = "local"
    
    # CORS
    CORS_ORIGINS: str = "*"

    @property
    def cors_origins_list(self) -> list[str]:
        return [o.strip() for o in self.CORS_ORIGINS.split(",")]

    # Database
    DATABASE_URL: str = "{self.ctx.all_env_vars.get('DATABASE_URL', '')}"
    DATABASE_READ_URL: str = "{self.ctx.all_env_vars.get('DATABASE_READ_URL', self.ctx.all_env_vars.get('DATABASE_URL', ''))}"

    # Cache
    CACHE_URL: str = "{self.ctx.all_env_vars.get('CACHE_URL', self.ctx.all_env_vars.get('REDIS_URL', ''))}"

    # Messaging
    KAFKA_BOOTSTRAP_SERVERS: str = "{self.ctx.all_env_vars.get('KAFKA_BOOTSTRAP_SERVERS', 'redpanda:9092')}"
    
    # Observability
    OTEL_EXPORTER_OTLP_ENDPOINT: str = "{self.ctx.all_env_vars.get('OTEL_EXPORTER_OTLP_ENDPOINT', '')}"
    
    # Secrets
    VAULT_ADDR: str = "{self.ctx.all_env_vars.get('VAULT_ADDR', '')}"
    VAULT_TOKEN: str = "{self.ctx.all_env_vars.get('VAULT_TOKEN', '')}"
    MINIO_ENDPOINT: str = "{self.ctx.all_env_vars.get('MINIO_ENDPOINT', 'minio:9000')}"
    MINIO_ACCESS_KEY: str = "{self.ctx.all_env_vars.get('MINIO_ROOT_USER', 'minioadmin')}"
    MINIO_SECRET_KEY: str = "{self.ctx.all_env_vars.get('MINIO_ROOT_PASSWORD', 'minioadmin')}"
    ELASTICSEARCH_URL: str = "{self.ctx.all_env_vars.get('ELASTICSEARCH_URL', 'http://elasticsearch:9200')}"
    NEO4J_URI: str = "{self.ctx.all_env_vars.get('NEO4J_URI', 'bolt://neo4j:7687')}"
    NEO4J_USER: str = "neo4j"
    NEO4J_PASSWORD: str = "{self.ctx.all_env_vars.get('NEO4J_PASSWORD', 'password')}"
    CLICKHOUSE_URL: str = "{self.ctx.all_env_vars.get('CLICKHOUSE_URL', 'clickhouse://default:@clickhouse:9000/default')}"
    QDRANT_URL: str = "http://qdrant:6333"
    TEMPORAL_TARGET: str = "temporal:7233"
    SMTP_HOST: str = "smtp.example.com"
    SMTP_PORT: int = 587
    SMTP_USER: str = "user"
    SMTP_PASSWORD: str = "password"
    NGROK_AUTHTOKEN: str = "{self.ctx.all_env_vars.get('NGROK_AUTHTOKEN', '')}"

    # Feature Flags
    UNLEASH_API_URL: str = "{self.ctx.all_env_vars.get('UNLEASH_API_URL', 'http://unleash:4242/api')}"
    UNLEASH_API_TOKEN: str = "{self.ctx.all_env_vars.get('UNLEASH_API_TOKEN', 'default-token')}"
    UNLEASH_APP_NAME: str = "{project}"

    model_config = SettingsConfigDict(
        env_file=".env", 
        env_file_encoding="utf-8",
        extra="ignore"
    )

settings = Settings()
'''
        files.append(("app/config.py", config_py))

        # Also update the main.py to use cors_origins_list property
        # The main.py template already references settings.CORS_ORIGINS
        # We need to update the CORS middleware line


        # 3. app/core/database.py
        if has_db:
            db_session = '''"""
SQLAlchemy async session management with pooling, health checks, and query helpers.
"""

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
from sqlalchemy import text, select
from config import settings
from typing import TypeVar, Type, Optional, List, Any

# Primary engine for Writes
engine = create_async_engine(
    settings.DATABASE_URL.replace("postgres://", "postgresql+asyncpg://"),
    pool_pre_ping=True,
    pool_size=20,
    max_overflow=10,
)

# Replica engine for Reads (defaults to primary if no replica is configured)
read_engine = create_async_engine(
    settings.DATABASE_READ_URL.replace("postgres://", "postgresql+asyncpg://"),
    pool_pre_ping=True,
    pool_size=20,
    max_overflow=10,
)

# Read/Write Session (Primary)
AsyncSessionLocal = async_sessionmaker(
    bind=engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

# Read-Only Session (Replica)
AsyncReadSessionLocal = async_sessionmaker(
    bind=read_engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

async def get_db():
    """Dependency for Read/Write operations."""
    async with AsyncSessionLocal() as session:
        try:
            yield session
        finally:
            await session.close()

async def get_read_db():
    """Dependency for Read-Only operations (optimized for replicas)."""
    async with AsyncReadSessionLocal() as session:
        try:
            yield session
        finally:
            await session.close()

T = TypeVar("T")

class BaseRepository:
    """Generic repository for common queries."""
    def __init__(self, model: Type[T], session: AsyncSession):
        self.model = model
        self.session = session

    async def get_by_id(self, id: Any) -> Optional[T]:
        return await self.session.get(self.model, id)

    async def get_all(self, skip: int = 0, limit: int = 100) -> List[T]:
        stmt = select(self.model).offset(skip).limit(limit)
        result = await self.session.execute(stmt)
        return list(result.scalars().all())

    async def create(self, obj_in: dict) -> T:
        db_obj = self.model(**obj_in)
        self.session.add(db_obj)
        await self.session.commit()
        await self.session.refresh(db_obj)
        return db_obj
'''
            files.append(("app/core/database.py", db_session))

        # 4. app/core/cache.py
        if has_cache:
            cache_py = '''"""
Cache client wrapper (Redis/Dragonfly).
"""

import redis.asyncio as redis
from config import settings
import logging
from typing import Any, AsyncGenerator

logger = logging.getLogger(__name__)

class CacheClient:
    def __init__(self, url: str):
        self._url = url
        self._client = None

    async def connect(self):
        if not self._client:
            self._client = redis.from_url(
                self._url,
                encoding="utf-8",
                decode_responses=True,
                socket_timeout=5,
            )
        await self._client.ping()

    async def disconnect(self):
        if self._client:
            await self._client.close()

    async def get(self, key: str) -> Any:
        return await self._client.get(key)

    async def set(self, key: str, value: str, expire: int = None) -> bool:
        return await self._client.set(key, value, ex=expire)

    async def delete(self, key: str) -> int:
        return await self._client.delete(key)
    
    async def ping(self):
        return await self._client.ping()

    def pipeline(self):
        """Return a pipeline object for bulk operations."""
        return self._client.pipeline()

    async def subscribe(self, channel: str) -> AsyncGenerator[dict, None]:
        """Pub/Sub subscriber generator."""
        pubsub = self._client.pubsub()
        await pubsub.subscribe(channel)
        async for message in pubsub.listen():
            if message['type'] == 'message':
                yield message

    async def publish(self, channel: str, message: str) -> int:
        """Pub/Sub publisher."""
        return await self._client.publish(channel, message)

cache = CacheClient(settings.CACHE_URL)
'''
            files.append(("app/core/cache.py", cache_py))

        # 5. app/core/messaging.py
        if has_messaging:
            messaging_py = '''"""
Kafka/RedPanda messaging service.
"""

import logging
from aiokafka import AIOKafkaProducer, AIOKafkaConsumer
from config import settings
import json
import asyncio

logger = logging.getLogger(__name__)

class MessagingService:
    def __init__(self, bootstrap_servers: str):
        self._bootstrap_servers = bootstrap_servers
        self._producer = None
        self._consumers = []

    async def start(self):
        self._producer = AIOKafkaProducer(
            bootstrap_servers=self._bootstrap_servers,
            value_serializer=lambda v: json.dumps(v).encode('utf-8')
        )
        await self._producer.start()

    async def stop(self):
        if self._producer:
            await self._producer.stop()
        for c in self._consumers:
            await c.stop()

    async def send_message(self, topic: str, message: dict):
        logger.info(f"Publishing to {topic}: {message}")
        try:
            await self._producer.send_and_wait(topic, message)
        except Exception as e:
            logger.error(f"Failed to send to {topic}: {e}. Sending to DLQ.")
            await self._send_dlq(topic, message, str(e))

    async def _send_dlq(self, original_topic: str, message: dict, error: str):
        dlq_topic = f"{original_topic}_dlq"
        dlq_message = {"original_message": message, "error": error}
        try:
            await self._producer.send_and_wait(dlq_topic, dlq_message)
        except Exception as dlq_err:
            logger.critical(f"DLQ delivery failed for {dlq_topic}: {dlq_err}")

    async def consume(self, topic: str, group_id: str, callback):
        """Start a consumer for a specific topic."""
        consumer = AIOKafkaConsumer(
            topic,
            bootstrap_servers=self._bootstrap_servers,
            group_id=group_id,
            value_deserializer=lambda m: json.loads(m.decode('utf-8'))
        )
        await consumer.start()
        self._consumers.append(consumer)
        
        asyncio.create_task(self._consume_loop(consumer, topic, callback))

    async def _consume_loop(self, consumer, topic, callback):
        try:
            async for msg in consumer:
                try:
                    await callback(msg.value)
                except Exception as e:
                    logger.error(f"Error processing message from {topic}: {e}")
                    await self._send_dlq(topic, msg.value, str(e))
        except Exception as e:
            logger.error(f"Consumer loop failed for {topic}: {e}")

kafka_service = MessagingService(settings.KAFKA_BOOTSTRAP_SERVERS)
'''
            files.append(("app/core/messaging.py", messaging_py))

        # 6. Core Drivers
        if has_storage:
            storage_py = '''"""MinIO/S3 storage client wrapper."""
import aioboto3
from config import settings
import logging

logger = logging.getLogger(__name__)

class StorageClient:
    def __init__(self):
        self.session = aioboto3.Session()
        self.config = {
            "endpoint_url": f"http://{settings.MINIO_ENDPOINT}",
            "aws_access_key_id": settings.MINIO_ACCESS_KEY,
            "aws_secret_access_key": settings.MINIO_SECRET_KEY,
        }

    async def get_client(self):
        return self.session.client("s3", **self.config)

    async def upload_file(self, file_path: str, bucket: str, object_name: str):
        async with await self.get_client() as s3:
            await s3.upload_file(file_path, bucket, object_name)

    async def generate_presigned_url(self, bucket: str, object_name: str, exp: int = 3600):
        async with await self.get_client() as s3:
            return await s3.generate_presigned_url('get_object', Params={'Bucket': bucket, 'Key': object_name}, ExpiresIn=exp)

storage_client = StorageClient()
'''
            files.append(("app/core/storage.py", storage_py))

        if has_search:
            search_py = '''"""Elasticsearch async client wrapper."""
from elasticsearch import AsyncElasticsearch
from config import settings

search_client = AsyncElasticsearch(settings.ELASTICSEARCH_URL)
'''
            files.append(("app/core/search.py", search_py))

        if has_neo4j:
            neo4j_py = '''"""Neo4j async driver wrapper."""
from neo4j import AsyncGraphDatabase
from config import settings

neo4j_driver = AsyncGraphDatabase.driver(settings.NEO4J_URI, auth=(settings.NEO4J_USER, settings.NEO4J_PASSWORD))
'''
            files.append(("app/core/neo4j.py", neo4j_py))

        if has_clickhouse:
            clickhouse_py = '''"""ClickHouse async client."""
import aiochclient
import aiohttp
from config import settings

class ClickHouseClient:
    async def get_client(self):
        session = aiohttp.ClientSession()
        return aiochclient.ChClient(session, url=settings.CLICKHOUSE_URL)

clickhouse_client = ClickHouseClient()
'''
            files.append(("app/core/clickhouse.py", clickhouse_py))

        if has_vector:
            vector_py = '''"""Qdrant vector search client."""
from qdrant_client import AsyncQdrantClient
from config import settings

vector_client = AsyncQdrantClient(url=settings.QDRANT_URL)
'''
            files.append(("app/core/vector.py", vector_py))

        if has_temporal:
            temporal_py = '''"""Temporal workflow client."""
from temporalio.client import Client
from config import settings

class TemporalClient:
    async def connect(self):
        return await Client.connect(settings.TEMPORAL_TARGET)

temporal_client = TemporalClient()
'''
            files.append(("app/core/temporal.py", temporal_py))

        if has_smtp:
            smtp_py = """\\"\\"\\"SMTP email client.\\"\\"\\"
import aiosmtplib
from email.message import EmailMessage
from config import settings

class SMTPClient:
    async def send(self, to: str, subject: str, content: str):
        message = EmailMessage()
        message["From"] = settings.SMTP_USER
        message["To"] = to
        message["Subject"] = subject
        message.set_content(content)
        await aiosmtplib.send(
            message,
            hostname=settings.SMTP_HOST,
            port=settings.SMTP_PORT,
            username=settings.SMTP_USER,
            password=settings.SMTP_PASSWORD,
            use_tls=True
        )

smtp_client = SMTPClient()
"""
            files.append(("app/core/smtp.py", smtp_py))

        if has_ngrok:
            tunnel_py = """\\"\\"\\"Ngrok tunnel starter for local dev.\\"\\"\\"
from pyngrok import ngrok
from config import settings

async def start_tunnel():
    if settings.NGROK_AUTHTOKEN:
        ngrok.set_auth_token(settings.NGROK_AUTHTOKEN)
    tunnel = ngrok.connect(8000)
    return tunnel.public_url
"""
            files.append(("app/core/tunnel.py", tunnel_py))

        # 7. app/routers/health.py
        health_py = f'''"""
Deep health checks for all system components.
"""

from fastapi import APIRouter, HTTPException
from config import settings
from sqlalchemy import text
import logging

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/health", tags=["monitoring"])

@router.get("/")
async def liveness():
    """Basic liveness probe."""
    return {{"status": "ok", "service": settings.APP_NAME}}

@router.get("/ready")
async def readiness():
    """Deep readiness probe checking all sub-systems."""
    checks = {{}}
    
    # 1. Database
    if {has_db}:
        try:
            from core.database import engine
            async with engine.connect() as conn:
                await conn.execute(text("SELECT 1"))
            checks["database"] = "healthy"
        except Exception as e:
            logger.error(f"Health check: Database failed: {{e}}")
            checks["database"] = "unhealthy"

    # 2. Cache
    if {has_cache}:
        try:
            from core.cache import cache
            await cache.ping()
            checks["cache"] = "healthy"
        except Exception as e:
            logger.error(f"Health check: Cache failed: {{e}}")
            checks["cache"] = "unhealthy"

    if "unhealthy" in checks.values():
        raise HTTPException(status_code=503, detail=checks)
        
    return {{"status": "ready", "checks": checks}}
'''
        files.append(("app/routers/__init__.py", ""))
        files.append(("app/routers/health.py", health_py))

        # 6.6 app/core/features.py
        if has_unleash:
            features_py = '''"""
Feature flag wrapper using Unleash.
"""
from UnleashClient import UnleashClient
from core.config import settings
import logging

logger = logging.getLogger(__name__)

client = UnleashClient(
    url=settings.UNLEASH_API_URL,
    app_name=settings.UNLEASH_APP_NAME,
    custom_headers={'Authorization': settings.UNLEASH_API_TOKEN},
    environment=settings.APP_ENV
)

def start_features():
    """Initialize the Unleash client."""
    try:
        client.initialize_client()
        logger.info("Unleash client initialized successfully.")
    except Exception as e:
        logger.error(f"Failed to initialize Unleash client: {e}")

def is_enabled(flag_name: str, context: dict = None, default_value: bool = False) -> bool:
    """Check if a feature flag is enabled."""
    return client.is_enabled(flag_name, context, default_value)
'''
            files.append(("app/core/features.py", features_py))

        # 6.8 app/core/ml/tracking.py
        if has_mlflow:
            tracking_py = '''"""
MLflow experiment tracking client.
"""
import mlflow
import os
from core.config import settings

def init_tracking():
    """Initialize MLflow tracking URI and default experiment."""
    mlflow.set_tracking_uri(settings.MLFLOW_TRACKING_URI)
    # Ensure S3 endpoint is set for artifacts
    os.environ["MLFLOW_S3_ENDPOINT_URL"] = settings.MLFLOW_S3_ENDPOINT_URL
    os.environ["AWS_ACCESS_KEY_ID"] = settings.MINIO_ACCESS_KEY
    os.environ["AWS_SECRET_ACCESS_KEY"] = settings.MINIO_SECRET_KEY
    
    mlflow.set_experiment(settings.APP_NAME)
'''
            files.append(("app/core/ml/__init__.py", ""))
            files.append(("app/core/ml/tracking.py", tracking_py))

        # 6.9 app/core/ml/observability.py
        if has_mlops_observability:
            obs_py = '''"""
ML Observability and Tracing configuration.
"""
import os
import logging
from core.config import settings

logger = logging.getLogger(__name__)

def init_observability(app=None):
    """Initialize LangFuse, Arize Phoenix, and Evidently integrations."""
'''
            if has_langfuse:
                obs_py += '''
    # Configure LangFuse
    os.environ["LANGFUSE_PUBLIC_KEY"] = getattr(settings, "LANGFUSE_PUBLIC_KEY", "")
    os.environ["LANGFUSE_SECRET_KEY"] = getattr(settings, "LANGFUSE_SECRET_KEY", "")
    os.environ["LANGFUSE_HOST"] = getattr(settings, "LANGFUSE_HOST", "http://localhost:3000")
    logger.info("LangFuse client configured")
'''
            if has_arize:
                obs_py += '''
    # Configure Arize Phoenix fastAPI instrumentation
    try:
        from openinference.instrumentation.fastapi import FastAPIInstrumentor
        from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
        from opentelemetry.sdk.trace import TracerProvider
        from opentelemetry.sdk.trace.export import SimpleSpanProcessor
        
        endpoint = getattr(settings, "PHOENIX_COLLECTOR_ENDPOINT", "http://localhost:6006/v1/traces")
        tracer_provider = TracerProvider()
        tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(endpoint)))
        
        if app:
            FastAPIInstrumentor().instrument_app(app, tracer_provider=tracer_provider)
            logger.info(f"Arize Phoenix fastAPI instrumentation attached to {endpoint}")
    except ImportError:
        logger.warning("openinference-instrumentation-fastapi not installed. Phoenix tracing disabled.")
'''
            if has_evidently:
                obs_py += '''
    # Configure Evidently UI connections (Placeholder for dataset routing)
    os.environ["EVIDENTLY_HOST"] = getattr(settings, "EVIDENTLY_HOST", "http://localhost:8085")
    logger.info("Evidently AI integration ready")
'''
            if "app/core/ml/__init__.py" not in [f[0] for f in files]:
                files.append(("app/core/ml/__init__.py", ""))
            files.append(("app/core/ml/observability.py", obs_py))

        # 6.10 app/core/ml/caching.py
        if has_gptcache:
            caching_py = '''"""
Semantic Caching using GPTCache.
"""
from gptcache import cache
from gptcache.manager import get_data_manager, CacheBase, VectorBase
from gptcache.processor.pre import get_prompt

def init_gptcache():
    """Initialize GPTCache with local or remote vector store."""
    data_manager = get_data_manager(CacheBase("sqlite"), VectorBase("faiss", dimension=1536))
    cache.init(
        pre_embedding_func=get_prompt,
        data_manager=data_manager,
    )
'''
            if "app/core/ml/__init__.py" not in [f[0] for f in files]:
                files.append(("app/core/ml/__init__.py", ""))
            files.append(("app/core/ml/caching.py", caching_py))

        # 6.11 app/core/ml/agents.py
        if has_agents:
            agents_py = '''"""
Agent Framework Configuration.
"""
import logging
from core.config import settings

logger = logging.getLogger(__name__)

def get_agent_executor():
    """Return the configured agent executor."""
    logger.info("Initializing Agent framework...")
    # Setup for LangChain / LlamaIndex / Haystack goes here
    return None
'''
            if "app/core/ml/__init__.py" not in [f[0] for f in files]:
                files.append(("app/core/ml/__init__.py", ""))
            files.append(("app/core/ml/agents.py", agents_py))

        # 6.7 app/core/limiter.py
        if has_limiter:
            limiter_py = '''"""
Distributed rate limiting using FastAPI-Limiter and Redis.
"""
from fastapi_limiter import FastAPILimiter
from core.config import settings
from core.cache import cache

async def init_limiter(app):
    """Initialize FastAPILimiter with the existing Redis connection."""
    # Ensure cache is connected
    if not cache.redis:
        await cache.connect()
    
    await FastAPILimiter.init(cache.redis)
'''
            files.append(("app/core/limiter.py", limiter_py))

        # 6.5 app/seeds.py
        if has_db:
            seed_py = '''"""
Database seeding script using Faker.
Run with `nikame db seed`.
"""
import asyncio
import logging
import json
import os
from faker import Faker
from core.database import AsyncSessionLocal
from sqlalchemy import text

# Add your models here
# from api.auth.models import User

logger = logging.getLogger(__name__)
fake = Faker()

async def seed_data():
    """Seed the database with initial data."""
    async with AsyncSessionLocal() as session:
        logger.info("Starting database seeding...")
        
        # Example: Seed Users
        # for _ in range(10):
        #     user = User(
        #         email=fake.unique.email(),
        #         username=fake.unique.user_name(),
        #         password_hash="argon2_placeholder_hash", # Replace with real hash
        #         is_active=True
        #     )
        #     session.add(user)
        
        # Example: Raw SQL seed
        # await session.execute(text("INSERT INTO ..."))
        
        await session.commit()
        logger.info("Successfully seeded database.")

if __name__ == "__main__":
    # Configure basic logging for the script
    logging.basicConfig(level=logging.INFO)
    asyncio.run(seed_data())
'''
            files.append(("app/seeds.py", seed_py))

        # 7. app/Dockerfile
        dockerfile = f'''# Multi-stage production-ready Dockerfile for FastAPI
FROM python:3.11-slim as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends curl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /install /usr/local
COPY . .
RUN useradd -m appuser && chown -R appuser /app
USER appuser
EXPOSE {self.port}
HEALTHCHECK --interval=15s --timeout=5s --retries=3 CMD curl -f http://localhost:{self.port}/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "{self.port}", "--proxy-headers", "--forwarded-allow-ips", "*"]
'''
        files.append(("app/Dockerfile", dockerfile))
        # 8. app/pyproject.toml
        pyproject = f'''[project]
name = "{project}"
version = "0.1.0"
description = "FastAPI application generated by NIKAME"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "fastapi>=0.109.0,<1.0.0",
    "uvicorn[standard]>=0.27.0,<1.0.0",
    "pydantic-settings>=2.1.0,<3.0.0",
    "httpx>=0.26.0,<1.0.0",
    "python-logging-loki>=0.3.1",
    "python-dotenv>=1.0.0",
]

[project.optional-dependencies]
db = [
    "sqlalchemy>=2.0.0,<3.0.0",
    "asyncpg>=0.29.0,<1.0.0",
    "alembic>=1.13.0,<2.0.0",
    "Faker>=23.0.0,<24.0.0",
]
cache = ["redis>=5.0.0,<6.0.0"]
messaging = ["aiokafka>=0.10.0,<1.0.0"]
features = ["unleash-client-python>=5.10.0"]
ratelimit = ["fastapi-limiter>=0.1.5,<0.2.0"]
storage = ["boto3>=1.34.0"]
vector = ["qdrant-client>=1.7.0"]
ml = ["mlflow>=2.10.0", "litellm>=1.10.0"]
obs_ml = []


[tool.uv]
managed = true
'''
        files.append(("app/pyproject.toml", pyproject))

        # 9. app/requirements.txt (Legacy compatibility)
        reqs = [
            "# Auto-generated by NIKAME",
            "fastapi>=0.109.0,<1.0.0",
            "uvicorn[standard]>=0.27.0,<1.0.0",
            "pydantic-settings>=2.1.0,<3.0.0",
            "httpx>=0.26.0,<1.0.0",
            "python-logging-loki>=0.3.1"
        ]
        if has_db:
            reqs.extend([
                "sqlalchemy>=2.0.0,<3.0.0",
                "asyncpg>=0.29.0,<1.0.0",
                "alembic>=1.13.0,<2.0.0",
                "Faker>=23.0.0,<24.0.0"
            ])

        if has_cache:
            reqs.append("redis>=5.0.0,<6.0.0")
        if has_messaging:
            reqs.append("aiokafka>=0.10.0,<1.0.0")
        if has_ngrok:
            reqs.append("pyngrok>=7.0.0")
        if has_unleash:
            reqs.append("unleash-client-python>=5.10.0")
        if has_limiter:
            reqs.append("fastapi-limiter>=0.1.5,<0.2.0")
        if has_mlflow:
            reqs.extend(["mlflow>=2.10.0", "litellm>=1.10.0"])
        if has_storage:
            reqs.append("boto3>=1.34.0")
        if has_vector:
            reqs.append("qdrant-client>=1.7.0")
        if has_langfuse:
            reqs.append("langfuse>=2.0.0")
        if has_arize:
            reqs.extend(["openinference-instrumentation-fastapi>=0.1.0", "opentelemetry-sdk>=1.20.0", "opentelemetry-exporter-otlp>=1.20.0"])
        if has_evidently:
            reqs.append("evidently>=0.4.0")
        if getattr(self, "has_gptcache", False):
            reqs.append("gptcache>=0.1.43")
        if has_langchain:
            reqs.append("langchain>=0.1.0")
        if has_llamaindex:
            reqs.append("llama-index>=0.10.0")
        if has_haystack:
            reqs.append("farm-haystack>=1.25.0")
            
        files.append(("app/requirements.txt", "\n".join(sorted(set(reqs))) + "\n"))
        files.append(("app/.dockerignore", "__pycache__\n*.pyc\n.env\n.venv\n.git\ntests/\nalembic/\n"))

        return files

    def requirements(self) -> list[str]:
        """Base API requirements."""
        return [
            "fastapi",
            "uvicorn[standard]",
            "pydantic-settings",
            "python-multipart",
            "slowapi",
        ]

    def lifespan_hooks(self) -> dict[str, list[Any]]:
        """API lifespan hooks."""
        return {"startup": [], "shutdown": []}

    def health_fn(self) -> str | None:
        """API health."""
        return "True"
