# backend/app/main.py
import asyncio
import contextlib
import logging
from contextlib import asynccontextmanager

from fastapi import APIRouter, FastAPI
from fastapi.middleware.cors import CORSMiddleware

from .config import get_settings
from .db import engine, init_db
from .routers import (
    _test,
    approvals,
    automation,
    calendar,
    connectors,
    dashboard,
    inbox,
    journey,
    llm,
    mail,
    notify,
    ops,
    people,
    tasks,
    tree,
    worker,
)
from .routers import (
    auth as auth_router,
)
from .routers import (
    settings as settings_router,
)

settings = get_settings()
health_router = APIRouter()  # 내부 prefix 없음. /health → /api/health


def _register_event_bus() -> None:
    """전역 event_bus 에 evaluator/suggester 핸들러 등록(앱 런타임 전용).
    테스트는 lifespan 을 띄우지 않으므로 전역 버스에 구독자가 없어 결정적이다."""
    from sqlmodel import Session

    from .automation import evaluator, suggester
    from .automation.event_bus import bus

    def session_factory():
        return Session(engine)

    evaluator.register(bus, session_factory)
    suggester.register(bus, session_factory)

    # phase-14: 결재 승인(approval.executed) → 승인된 심부름 이어 실행
    from .worker.main import register_event_subscribers

    register_event_subscribers(bus, session_factory)


async def _auto_sync_loop() -> None:
    """앱 내장 자동 풀링: 주기적으로 연결된 real 계정(메일·일정)을 증분 sync.
    blocking httpx/DB 작업이라 to_thread 로 실행해 이벤트 루프를 막지 않는다."""
    from .worker.sync_jobs import run_periodic_sync

    log = logging.getLogger("ari.autosync")
    interval = max(5, settings.auto_sync_interval_seconds)
    await asyncio.sleep(15)  # 기동 직후 한 번(초기 연결 sync 와 겹치지 않게 약간 지연)
    while True:
        try:
            results = await asyncio.to_thread(run_periodic_sync)
            new = sum(r.get("upserted", 0) for r in results)
            if new:
                log.info("auto-sync: %d new records across %d accounts", new, len(results))
        except asyncio.CancelledError:
            raise
        except Exception:
            log.exception("auto-sync failed; retrying next interval")
        await asyncio.sleep(interval)


@asynccontextmanager
async def lifespan(app: FastAPI):
    from .observability.logging import configure_logging

    configure_logging(settings.log_level)
    init_db()  # 개발 편의(운영은 alembic). 테이블 보장.
    _register_event_bus()
    # 설정 페이지의 LLM 오버레이(app_setting)를 인메모리로 적재(앱 런타임 전용).
    from sqlmodel import Session

    from . import runtime_config

    with Session(engine) as _s:
        runtime_config.load_from_db(_s)
    sync_task = asyncio.create_task(_auto_sync_loop()) if settings.auto_sync_enabled else None
    try:
        yield
    finally:
        if sync_task:
            sync_task.cancel()
            with contextlib.suppress(asyncio.CancelledError):
                await sync_task


app = FastAPI(title="아리 Ari API", lifespan=lifespan)
app.add_middleware(
    CORSMiddleware,
    allow_origins=[o.strip() for o in settings.frontend_origin.split(",")],
    allow_methods=["*"],
    allow_headers=["*"],
    allow_credentials=True,
)
# phase-15: 요청 컨텍스트(request_id) + 접근 로그 + 메트릭
from .observability.middleware import RequestContextMiddleware  # noqa: E402

app.add_middleware(RequestContextMiddleware)


# health: 라우터 내부 prefix 없이 "/health" 로 정의 → prefix="/api" 등록 시 /api/health.
@health_router.get("/health")
def health():
    return {"status": "ok"}


# 모든 라우터를 prefix="/api" 로 등록(라우터 내부 prefix 없음).
app.include_router(health_router, prefix="/api", tags=["health"])
app.include_router(people.router, prefix="/api", tags=["people"])
app.include_router(tree.router, prefix="/api", tags=["tree"])
app.include_router(tasks.router, prefix="/api", tags=["tasks"])
app.include_router(inbox.router, prefix="/api", tags=["inbox"])
app.include_router(dashboard.router, prefix="/api", tags=["dashboard"])
app.include_router(llm.router, prefix="/api", tags=["llm"])
# phase-7: 자율성 코어
app.include_router(approvals.router, prefix="/api", tags=["approvals"])
app.include_router(automation.router, prefix="/api", tags=["automation"])
# phase-8: 일정 + 회의
app.include_router(calendar.router, prefix="/api", tags=["calendar"])
# phase-9: 메일 + 알림
app.include_router(mail.router, prefix="/api", tags=["mail"])
app.include_router(notify.router, prefix="/api", tags=["notify"])
# phase-12: 여정
app.include_router(journey.router, prefix="/api", tags=["journey"])

app.include_router(connectors.router, prefix="/api", tags=["connectors"])

app.include_router(worker.router, prefix="/api", tags=["worker"])

app.include_router(auth_router.router, prefix="/api", tags=["auth"])
app.include_router(settings_router.router, prefix="/api", tags=["settings"])
app.include_router(ops.router, prefix="/api", tags=["ops"])
# 테스트 전용 리셋(ARI_ALLOW_TEST_RESET=1 가드, 운영 403)
app.include_router(_test.router, prefix="/api", tags=["test"])
