"""
cortex/hippo/server.py — Standalone Hippo server runner.

Usage:
    python -m cortex.hippo.server [--host 127.0.0.1] [--port 8850] [--db-path ~/.openclaw/hippo.db]

Starts a uvicorn server exposing Hippo's engine + tools via FastAPI.
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import os
import sys
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Optional

import uvicorn
from fastapi import FastAPI, HTTPException

from cortex.hippo.config import HippoConfig
from cortex.hippo.engine import HippoEngine
from cortex.hippo.retrieval import RetrievalEngine
from cortex.hippo.router import create_hippo_router
from cortex.hippo.summarize import SummarizeOptions, SummarizeResult

logger = logging.getLogger("hippo.server")

# ---------------------------------------------------------------------------
# Summarize functions — stub + HTTP callback
# ---------------------------------------------------------------------------


async def _stub_summarize(
    text: str, aggressive: bool = False, options: Optional[SummarizeOptions] = None
) -> SummarizeResult:
    """Placeholder summarizer for standalone testing.

    Matches SummarizeFn signature: (text, aggressive, options) -> SummarizeResult.
    In production, use _http_summarize with a callback URL instead.
    """
    truncated = text[:500].rstrip()
    if len(text) > 500:
        truncated += "..."
    content = f"[stub-summary] {truncated}"
    return SummarizeResult(
        content=content,
        quality_score=0.5,
        quality_breakdown={"compression": 0.5, "coherence": 0.5, "coverage": 0.5},
        source="fallback",
        mode_used="aggressive" if aggressive else "normal",
    )


def _make_http_summarize(summarize_url: str):
    """Create a SummarizeFn that POSTs to a plugin callback URL.

    The callback server (typically the hippo-proxy OpenClaw plugin) receives:
        {system: str, messages: [{role, content}], max_tokens: int, temperature: float}
    And returns:
        {content: str}
    """

    async def _http_summarize(
        text: str, aggressive: bool = False, options: Optional[SummarizeOptions] = None
    ) -> SummarizeResult:
        import aiohttp

        options = options or SummarizeOptions()
        temperature = 0.3 if aggressive else 0.4
        max_tokens = 2048

        system = (
            "You are a lossless context compactor. Compress conversation "
            "segments into dense, factual summaries that preserve all actionable "
            "information. Output plain text only. No markdown headings, no bullet "
            "lists, no preamble."
        )

        try:
            async with aiohttp.ClientSession() as session:
                async with session.post(
                    summarize_url,
                    json={
                        "system": system,
                        "messages": [{"role": "user", "content": text}],
                        "max_tokens": max_tokens,
                        "temperature": temperature,
                        "aggressive": aggressive,
                        "depth": options.depth if options else 0,
                    },
                    timeout=aiohttp.ClientTimeout(total=120),
                ) as resp:
                    if resp.status != 200:
                        err_text = await resp.text()
                        logger.warning(
                            "Summarize callback returned %d: %s",
                            resp.status,
                            err_text[:200],
                        )
                        return await _stub_summarize(text, aggressive, options)

                    data = await resp.json()
                    content = data.get("content", "")
                    if not content:
                        return await _stub_summarize(text, aggressive, options)

                    return SummarizeResult(
                        content=content,
                        quality_score=0.8,
                        quality_breakdown={
                            "compression": 0.8,
                            "coherence": 0.8,
                            "coverage": 0.8,
                        },
                        source="content",
                        mode_used="aggressive" if aggressive else "normal",
                    )
        except Exception as e:
            logger.warning("Summarize callback failed: %s", e)
            return await _stub_summarize(text, aggressive, options)

    return _http_summarize


# ---------------------------------------------------------------------------
# Global state (populated in lifespan)
# ---------------------------------------------------------------------------

_state: dict = {}


# ---------------------------------------------------------------------------
# App factory
# ---------------------------------------------------------------------------


def create_server_app(
    db_path: str = "~/.openclaw/hippo.db",
    owner_id: str = "default",
    summarize_url: Optional[str] = None,
) -> FastAPI:
    """Build the FastAPI app with lifespan-managed Hippo engine."""

    resolved_db = str(Path(db_path).expanduser())

    @asynccontextmanager
    async def lifespan(app: FastAPI):
        logger.info(f"Initializing Hippo engine (db={resolved_db}, owner={owner_id})")

        # Ensure log directory exists
        log_dir = Path(resolved_db).parent
        log_dir.mkdir(parents=True, exist_ok=True)

        config = HippoConfig(db_path=resolved_db, enabled=True)
        summarize_fn = (
            _make_http_summarize(summarize_url) if summarize_url else _stub_summarize
        )
        if summarize_url:
            logger.info(f"Using HTTP summarize callback: {summarize_url}")
        else:
            logger.info("Using stub summarizer (no --summarize-url provided)")
        engine = HippoEngine(config=config, summarize_fn=summarize_fn)
        await engine.initialize(owner_id=owner_id)

        retrieval = RetrievalEngine(
            conversation_store=engine.conversation_store,
            summary_store=engine.summary_store,
            db=engine._db,
            config=config,
        )

        _state["engine"] = engine
        _state["retrieval"] = retrieval
        _state["config"] = config
        _state["db_path"] = resolved_db

        # Mount the Hippo router dynamically
        router = create_hippo_router(
            engine=engine,
            assembler=engine.assembler,
            conversation_store=engine.conversation_store,
            summary_store=engine.summary_store,
            retrieval=retrieval,
            config=config,
            owner_id=owner_id,
        )
        app.include_router(router)

        logger.info("Hippo engine ready")
        yield

        # Shutdown
        if engine._db:
            logger.info("Shutting down Hippo engine...")
            await engine._db.close()
        _state.clear()

    app = FastAPI(
        title="Hippo LCM Server",
        version="1.5.6",
        description="Standalone Hippo context engine for OpenClaw integration",
        lifespan=lifespan,
    )

    @app.get("/health")
    async def health():
        engine = _state.get("engine")
        return {
            "status": "ok" if engine else "starting",
            "engine": "hippo",
            "db": resolved_db,
        }

    return app


# ---------------------------------------------------------------------------
# CLI entry point
# ---------------------------------------------------------------------------


def main():
    parser = argparse.ArgumentParser(description="Hippo LCM standalone server")
    parser.add_argument(
        "--host", default="127.0.0.1", help="Bind host (default: 127.0.0.1)"
    )
    parser.add_argument(
        "--port", type=int, default=8850, help="Bind port (default: 8850)"
    )
    parser.add_argument(
        "--db-path", default="~/.openclaw/hippo.db", help="SQLite database path"
    )
    parser.add_argument(
        "--owner-id", default="default", help="Owner ID for multi-tenant isolation"
    )
    parser.add_argument(
        "--summarize-url",
        default=None,
        help="HTTP callback URL for LLM summarization (e.g., http://127.0.0.1:18860/summarize)",
    )
    parser.add_argument(
        "--log-level",
        default="info",
        choices=["debug", "info", "warning", "error"],
    )
    args = parser.parse_args()

    logging.basicConfig(
        level=getattr(logging, args.log_level.upper()),
        format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )

    app = create_server_app(
        db_path=args.db_path,
        owner_id=args.owner_id,
        summarize_url=args.summarize_url,
    )

    uvicorn.run(
        app,
        host=args.host,
        port=args.port,
        log_level=args.log_level,
    )


if __name__ == "__main__":
    main()
