import os
import time

from fastapi import FastAPI, APIRouter
from uuid import uuid4
from typing import List
from contextlib import asynccontextmanager
from contextlib import AsyncExitStack
from typing import Callable


class SubApp:
    def __init__(self, name:str, lifespan:Callable):
        self.id = uuid4()
        self.name = name
        self.prefix = f"/api/{name}"
        self.lifespan = lifespan
        self.router = APIRouter()
    
    def __str__(self):
        return f"SubApp(name={self.name}, prefix={self.prefix}, id={self.id})"

class MainApp:
    def __init__(self, sub_apps: List[SubApp]):
        @asynccontextmanager
        async def lifespan(app: FastAPI):
            async with AsyncExitStack() as stack:
                # [perf] per-lifespan boot timing. Without this the packaged backend.log has no per-SubApp markers and a cold-start stall can only be guessed at. One perf_counter + flushed print per app pins exactly which lifespan (or the cold first-touch I/O entering it) dominates.
                p_boot_t0 = time.perf_counter()
                for sub_app in sub_apps:
                    p_t0 = time.perf_counter()
                    await stack.enter_async_context(sub_app.lifespan())
                    p_dt = (time.perf_counter() - p_t0) * 1000
                    if p_dt > 50:  # only flag a slow lifespan; keeps boot logs quiet
                        print(f"[perf] lifespan {sub_app.name} t={p_dt:.0f}ms", flush=True)
                print(f"[perf] lifespans-total t={(time.perf_counter() - p_boot_t0) * 1000:.0f}ms", flush=True)
                p_port = os.environ.get("OPENSWARM_PORT", "8324")
                print(f"\nCheck out the API docs at: http://127.0.0.1:{p_port}/docs\n")
                yield
                
        self.app = FastAPI(lifespan=lifespan)

        for sub_app in sub_apps:
            self.app.include_router(
                sub_app.router, 
                prefix=sub_app.prefix,
                tags=[sub_app.name]
            )