"""Chat completions endpoint — delegates to CompletionPipeline.

The endpoint is a thin wrapper; all logic lives in _pipeline.py.
"""

from __future__ import annotations

import json

import httpx
import litellm
from fastapi import APIRouter, Header, HTTPException, Request
from fastapi.responses import JSONResponse, StreamingResponse

from proxym.api._pipeline import CompletionPipeline
from proxym.api._state import get_router
from proxym.config import get_settings
from proxym.providers import MODELS, TaskTier
from proxym.router import analyze_request
from proxym.router.strategy import CostLedger

router = APIRouter()
ledger = CostLedger()


def _resolve_model(model: str | None) -> str | None:
    if not model:
        return model
    return get_settings().resolve_model_alias(model)


def _analyze_request(messages, x_task_tier: str | None = None):
    return analyze_request(messages, explicit_tier=x_task_tier)


def _route_to_model(messages, model: str | None = None, x_task_tier: str | None = None):
    resolved = _resolve_model(model)
    if resolved and resolved in MODELS:
        spec = MODELS[resolved]
        return spec.litellm_model, spec.provider
    analysis = _analyze_request(messages, x_task_tier)
    if isinstance(analysis, str):
        tier_map = {
            "simple": TaskTier.TRIVIAL,
            "trivial": TaskTier.TRIVIAL,
            "complex": TaskTier.COMPLEX,
            "deep": TaskTier.DEEP_REASONING,
            "standard": TaskTier.STANDARD,
        }
        analysis = analyze_request(messages, explicit_tier=tier_map.get(analysis, TaskTier.STANDARD))
    decision = get_router().route(analysis)
    return decision.model.litellm_model, decision.model.provider


async def completion(**kwargs):
    if kwargs.get("stream"):
        return litellm.acompletion(**kwargs)
    return await litellm.acompletion(**kwargs)


def _usage_tokens(resp) -> tuple[int, int]:
    usage = getattr(resp, "usage", None)
    if usage is None and isinstance(resp, dict):
        usage = resp.get("usage")
    prompt_tokens = getattr(usage, "prompt_tokens", 0) if usage is not None else 0
    completion_tokens = getattr(usage, "completion_tokens", 0) if usage is not None else 0
    if isinstance(usage, dict):
        prompt_tokens = usage.get("prompt_tokens", 0)
        completion_tokens = usage.get("completion_tokens", 0)
    return prompt_tokens or 0, completion_tokens or 0


def _response_to_dict(resp):
    if hasattr(resp, "model_dump"):
        return resp.model_dump()
    if isinstance(resp, dict):
        return resp
    if hasattr(resp, "choices"):
        content = getattr(getattr(getattr(resp.choices[0], "message", None), "content", None), "strip", lambda: getattr(getattr(resp.choices[0], "message", None), "content", ""))()
        return {
            "choices": [
                {
                    "message": {
                        "content": content,
                        "tool_calls": getattr(getattr(resp.choices[0], "message", None), "tool_calls", None),
                    }
                }
            ],
            "usage": {
                "prompt_tokens": _usage_tokens(resp)[0],
                "completion_tokens": _usage_tokens(resp)[1],
            },
            "model": getattr(resp, "model", None),
        }
    return dict(resp)


@router.post("/v1/chat/completions")
async def chat_completions(
    request: Request,
    x_task_tier: str | None = Header(None, alias="X-Task-Tier"),
):
    """OpenAI-compatible chat completions with intelligent routing.

    Extra headers:
      X-Task-Tier: trivial|operational|standard|complex|deep
      X-Inject-Context: true  (inject latest delta context)
      X-Working-Directory: /path/to/project  (project detection)
      X-Session-Id: abc123  (session continuity)
      X-Enable-Tools: true  (inject MCP tools based on context)
    """
    try:
        body = await request.json()
    except json.JSONDecodeError as exc:
        raise HTTPException(status_code=400, detail="invalid json") from exc

    if not body.get("messages"):
        raise HTTPException(status_code=422, detail="messages must not be empty")

    if request.headers.get("x-enable-tools", "").lower() == "true" or request.headers.get("x-inject-context", "").lower() == "true":
        pipeline = CompletionPipeline(body, x_task_tier, headers=dict(request.headers))
        response = await pipeline.execute()
        if isinstance(response, JSONResponse) and "X-Cost-Usd" in response.headers:
            response.headers["X-Proxym-Cost"] = response.headers["X-Cost-Usd"]
        return response

    model, _provider = _route_to_model(body.get("messages", []), body.get("model"), x_task_tier)
    kwargs = {k: v for k, v in body.items() if k != "model"}
    kwargs["model"] = model

    settings = get_settings()
    fallback_model = settings.resolve_model_alias("cheap")

    try:
        if body.get("stream"):
            stream_resp = await completion(**kwargs)

            async def _stream():
                async for chunk in stream_resp:
                    payload = chunk.model_dump() if hasattr(chunk, "model_dump") else dict(chunk)
                    yield f"data: {json.dumps(payload)}\n\n"
                yield "data: [DONE]\n\n"

            return StreamingResponse(_stream(), media_type="text/event-stream")

        resp = await completion(**kwargs)
    except httpx.HTTPStatusError as exc:
        status_code = getattr(exc.response, "status_code", 500)
        if status_code in (429, 500):
            kwargs["model"] = fallback_model if fallback_model != model else settings.resolve_model_alias("balanced")
            try:
                resp = await completion(**kwargs)
            except Exception as inner:
                raise HTTPException(status_code=503, detail=str(inner)) from inner
        else:
            raise HTTPException(status_code=502, detail=str(exc)) from exc
    except Exception as exc:
        raise HTTPException(status_code=503, detail=str(exc)) from exc

    prompt_tokens, completion_tokens = _usage_tokens(resp)
    cost = ((prompt_tokens + completion_tokens) / 1_000_000) if (prompt_tokens or completion_tokens) else 0.0
    ledger.record(cost)
    data = _response_to_dict(resp)
    return JSONResponse(
        content=data,
        headers={
            "X-Proxym-Cost": f"{cost:.6f}",
            "X-Cost-Usd": f"{cost:.6f}",
        },
    )
