# gateway.py — FastAPI + Moodle + Gemini + RAG

from __future__ import annotations

import os
import re
import logging
from pathlib import Path

import requests
from dotenv import load_dotenv

from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel

from rag import Retriever, build_prompt


# ==========================================================
# CONFIG
# ==========================================================

BASE_DIR = Path(__file__).resolve().parent
load_dotenv(BASE_DIR / ".env")

MODEL_API_URL = os.getenv("MODEL_API_URL", "").strip()
MODEL_API_KEY = os.getenv("MODEL_API_KEY", "").strip()

MODEL_NAME = os.getenv(
    "MODEL_MODEL",
    os.getenv("MODEL_NAME", "gemini-2.5-flash")
).strip()

REQUEST_TIMEOUT = int(os.getenv("REQUEST_TIMEOUT", "30"))

ALLOWED_ORIGINS = [
    x.strip()
    for x in os.getenv("ALLOWED_ORIGINS", "*").split(",")
    if x.strip()
]


# ==========================================================
# LOGGING
# ==========================================================

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s"
)

logger = logging.getLogger("assistant")


# ==========================================================
# FASTAPI
# ==========================================================

app = FastAPI(
    title="Assistant Gateway",
    version="1.0.0"
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=ALLOWED_ORIGINS if ALLOWED_ORIGINS else ["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

session = requests.Session()


# ==========================================================
# REQUEST MODEL
# ==========================================================

class ChatRequest(BaseModel):
    courseid: int
    question: str
    course_name: str = "Curso Moodle"


# ==========================================================
# HEALTH
# ==========================================================

@app.get("/")
def home():
    return {
        "status": "ok",
        "service": "assistant",
        "model": MODEL_NAME,
    }


@app.get("/health")
def health():
    return {
        "ok": True
    }


@app.get("/courses/{courseid}/rag-status")
def rag_status(courseid: int):

    try:
        retriever = Retriever(courseid)

        chunks = len(retriever.chunks)

        return {
            "ok": True,
            "courseid": courseid,
            "chunks": chunks,
            "rag_available": chunks > 0,
        }

    except Exception:

        return {
            "ok": False,
            "courseid": courseid,
            "chunks": 0,
            "rag_available": False,
        }


# ==========================================================
# CLEAN ANSWER
# ==========================================================

def clean_answer(text: str) -> str:

    text = text or ""

    text = text.replace("###", "")
    text = text.replace("**", "")
    text = text.replace("__", "")

    text = re.sub(r"\n{3,}", "\n\n", text)

    text = text.strip()

    if len(text) > 1500:
        text = text[:1500].strip() + "..."

    return text


# ==========================================================
# GEMINI CLIENT
# ==========================================================

def call_llm_openai(prompt: str) -> str:

    if not MODEL_API_URL:
        return "MODEL_API_URL não configurado."

    if not MODEL_API_KEY:
        return "MODEL_API_KEY não configurado."

    if not MODEL_NAME:
        return "MODEL_MODEL não configurado."

    headers = {
        "Authorization": f"Bearer {MODEL_API_KEY}",
        "Content-Type": "application/json",
    }

    payload = {
        "model": MODEL_NAME,
        "messages": [
            {
                "role": "system",
                "content": (
                    "Você é um tutor virtual inteligente integrado ao Moodle. "
                    "Responda como professor universitário experiente. "
                    "Explique os conceitos de forma natural, clara, resumida e didática. "
                    "Use apenas as informações presentes no contexto do curso. "
                    "Nunca copie literalmente os trechos do material. "
                    "Nunca mostre fontes, chunks, JSON, rankings, URLs ou metadados técnicos. "
                    "Nunca diga 'o material contém'. "
                    "Responda diretamente à pergunta do aluno. "
                    "Quando houver pouca informação, explique isso de forma pedagógica."
                ),
            },
            {
                "role": "user",
                "content": prompt,
            },
        ],
        "temperature": 0.2,
    }

    try:

        response = session.post(
            MODEL_API_URL,
            headers=headers,
            json=payload,
            timeout=REQUEST_TIMEOUT,
        )

        if response.status_code >= 400:

            logger.error(response.text)

            raise HTTPException(
                status_code=502,
                detail=f"Erro Gemini: {response.status_code} - {response.text}",
            )

        data = response.json()

        answer = (
            data.get("choices", [{}])[0]
            .get("message", {})
            .get("content", "")
            .strip()
        )

        return clean_answer(
            answer or "O Gemini respondeu vazio."
        )

    except HTTPException:
        raise

    except Exception as e:

        logger.error(str(e))

        raise HTTPException(
            status_code=502,
            detail=f"Falha ao contactar Gemini: {e}",
        )


# ==========================================================
# CHAT ENDPOINT
# ==========================================================

@app.post("/chat")
def chat(req: ChatRequest):

    question = req.question.strip()

    if not question:
        raise HTTPException(
            status_code=400,
            detail="Pergunta vazia."
        )

    try:

        retriever = Retriever(req.courseid)

        passages = retriever.search(
            question,
            k=6
        )

        if not passages:

            return {
                "answer": (
                    "Não encontrei conteúdos relevantes "
                    "nos materiais deste curso."
                )
            }

        prompt = build_prompt(
            course_name=req.course_name,
            question=question,
            passages=passages,
        )

        answer = call_llm_openai(prompt)

        return {
            "answer": answer
        }

    except FileNotFoundError:

        raise HTTPException(
            status_code=404,
            detail=(
                f"O índice do curso {req.courseid} "
                f"não existe. Execute ingest.py primeiro."
            ),
        )

    except Exception as e:

        logger.error(str(e))

        raise HTTPException(
            status_code=500,
            detail=str(e)
        )


# ==========================================================
# ASK ALIAS
# ==========================================================

@app.post("/ask")
def ask(req: ChatRequest):
    return chat(req)


# ==========================================================
# MAIN
# ==========================================================

if __name__ == "__main__":

    import uvicorn

    uvicorn.run(
        "gateway:app",
        host="0.0.0.0",
        port=8000,
        reload=True,
    )
