import os

import httpx
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel


class PromptRequest(BaseModel):
    prompt: str
    stream: bool = False


app = FastAPI()
# Ollama lives on SOLARIA (GPU node); resolved via Tailscale MagicDNS on the
# host, which Docker's embedded DNS forwards to. Overridable per node via env.
OLLAMA_URL = os.environ.get('OLLAMA_URL', 'http://solaria:11434/api/generate')
CHAT_MODEL = os.environ.get('CHAT_MODEL', 'deepcoder:14b')
CODE_MODEL = os.environ.get('CODE_MODEL', 'deepseek-coder:latest')
NATURAL_LANGUAGE_STARTERS = {
    'write',
    'create',
    'generate',
    'make',
    'say',
    'show',
    'give',
    'explain',
    'tell',
    'build',
}


@app.get('/')
async def root() -> dict[str, str]:
    return {'status': 'gateway ok'}


async def generate(route: str, model: str, request: PromptRequest) -> dict[str, object]:
    payload = {
        'model': model,
        'prompt': request.prompt,
        'stream': False,
    }
    try:
        async with httpx.AsyncClient(timeout=120.0) as client:
            ollama_response = await client.post(OLLAMA_URL, json=payload)
            ollama_response.raise_for_status()
    except httpx.HTTPStatusError as exc:
        raise HTTPException(status_code=502, detail=f'Ollama error: {exc.response.text}') from exc
    except httpx.HTTPError as exc:
        raise HTTPException(status_code=502, detail=f'Ollama unreachable: {exc}') from exc

    raw = ollama_response.json()
    return {
        'route': route,
        'model': model,
        'response': raw.get('response', ''),
        'raw': raw,
    }


def looks_like_shell_command(prompt: str) -> bool:
    stripped = prompt.strip()
    if not stripped or '\n' in stripped:
        return False
    first_token = stripped.split()[0].lower()
    if first_token in NATURAL_LANGUAGE_STARTERS:
        return False
    return True


@app.post('/api/chat')
async def chat(request: PromptRequest) -> dict[str, object]:
    return await generate('chat', CHAT_MODEL, request)


@app.post('/api/code')
async def code(request: PromptRequest) -> dict[str, object]:
    prompt = request.prompt
    code_prompt = f"""
You are a coding and command assistant.

Return ONLY the final answer.
Do NOT explain.
Do NOT use markdown.
Do NOT mention that you are an AI.
Do NOT add prose.

If the task is a shell command request, return only the shell command.
If the task is a programming request, return only the code.

TASK:
{prompt}
""".strip()
    code_request = PromptRequest(prompt=code_prompt, stream=request.stream)
    result = await generate('code', CODE_MODEL, code_request)
    if looks_like_shell_command(prompt):
        result['response'] = prompt.strip()
    return result
