import hmac
import re
from contextlib import asynccontextmanager
from typing import Annotated
from uuid import uuid4

from fastapi import Depends, FastAPI, Header, Request
from fastapi.responses import JSONResponse

from .config import get_ai_settings
from .errors import AIError
from .providers.openai import OpenAIProvider
from .schemas import PlanRequest, PlanResult
from .supervisor import Supervisor
from .tools import default_registry


@asynccontextmanager
async def lifespan(_app: FastAPI):
    get_ai_settings().require_production_configuration()
    yield


app = FastAPI(
    title="Hayva AI Agent", version="0.1.0", docs_url=None, redoc_url=None,
    lifespan=lifespan,
)
REQUEST_ID_PATTERN = re.compile(r"^[A-Za-z0-9._:-]{1,80}$")
registry = default_registry()


def get_supervisor() -> Supervisor:
    return Supervisor(OpenAIProvider(get_ai_settings()), registry)


def require_service_token(
    x_service_token: Annotated[str | None, Header()] = None,
) -> None:
    expected = get_ai_settings().read_service_token()
    if not expected:
        raise AIError("SERVICE_AUTH_UNCONFIGURED", "Service authentication is unconfigured.", 503)
    if not x_service_token or not hmac.compare_digest(x_service_token, expected):
        raise AIError("SERVICE_AUTH_INVALID", "Service authentication failed.", 401)


ServiceAuth = Annotated[None, Depends(require_service_token)]
SupervisorDep = Annotated[Supervisor, Depends(get_supervisor)]


@app.middleware("http")
async def request_context(request: Request, call_next):
    candidate = request.headers.get("x-request-id", "")
    request_id = candidate if REQUEST_ID_PATTERN.fullmatch(candidate) else str(uuid4())
    request.state.request_id = request_id
    response = await call_next(request)
    response.headers["x-request-id"] = request_id
    return response


@app.exception_handler(AIError)
async def ai_error_handler(request: Request, error: AIError):
    return JSONResponse(status_code=error.status_code, content={"success": False, "error": {
        "code": error.code, "message": error.message, "request_id": request.state.request_id}})


@app.get("/health")
async def health(_auth: ServiceAuth):
    provider = OpenAIProvider(get_ai_settings())
    return {"status": "ok", "service": "ai-agent",
            "provider": "configured" if provider.configured else "unconfigured"}


@app.get("/v1/tools", dependencies=[Depends(require_service_token)])
async def list_tools():
    return {"success": True, "tools": [tool.public_contract() for tool in registry.list()]}


@app.post("/v1/plans", response_model=PlanResult)
async def create_plan(payload: PlanRequest, _auth: ServiceAuth, supervisor: SupervisorDep):
    return await supervisor.plan(payload)
