'''


https://synap.si/gemini/static/index.html


conda activate avatar
cd /home/cirillo/Desktop/GEMINI/GEMINI_RETAIL21
uvicorn main:app --reload --host 0.0.0.0 --port 9115




APRI LA PAGINA index.html ed imposta la porta identica a quella che vedi qui
http://localhost:9115/static/index.html


GOOGLE SHEET
ID implementazione
AKfycbzFsHcVfZztV3zq4guPqAs_Gz-R2jYYVv0RfB4SsOjmsyCQFZxZTyegbAOKIWrdfmdyng
URL
https://script.google.com/macros/s/AKfycbzFsHcVfZztV3zq4guPqAs_Gz-R2jYYVv0RfB4SsOjmsyCQFZxZTyegbAOKIWrdfmdyng/exec



SYNAP.SI SERVER

A2HOSTING
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/:$LD_LIBRARY_PATH
ssh -p 7822 root@68.66.251.32
synap.si77
sudo systemctl restart fastapi-gemini
journalctl -u fastapi-gemini -f


conda activate gemini
cd  


'''


import json
import pathlib
import asyncio
import datetime
import uuid

from fastapi import FastAPI, Depends, WebSocket, WebSocketDisconnect, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from starlette.middleware.base import BaseHTTPMiddleware
from dotenv import load_dotenv

from google import genai
from google.genai.types import (
    LiveConnectConfig,
    PrebuiltVoiceConfig,
    SpeechConfig,
    VoiceConfig,
    AudioTranscriptionConfig,
    Tool, FunctionResponse, 
    FunctionDeclaration, 
    StartSensitivity, 
    EndSensitivity, 
    CreateCachedContentConfig, 
    SafetySetting, 
    ThinkingConfig,
    Part,
    Schema,
    Content,
    Type)

import gemini_module
from gemini_module import connect_gemini, active_handlers, GeminiHandler
import httpx
from fastapi import Body
from fastapi.responses import FileResponse
import os


load_dotenv()

GEMINI_API_KEY = "AIzaSyCfh6w4ksAPbS1eKzxRyhK1SW6rwtYRdqk"
GEMINI_model="gemini-2.5-flash-preview-04-17"

# GOOGLE TT URL non viene usato, serve solo a configurare HEAD nel javascript 
GOOGLE_TTS_URL = "https://eu-texttospeech.googleapis.com/v1beta1/text:synthesize"

## url: 'https://models.readyplayer.me/64bfa15f0e72c63d7c3934a6.glb?morphTargets=ARKit,Oculus+Visemes,mouthOpen,mouthSmile,eyesClosed,eyesLookUp,eyesLookDown&textureSizeLimit=1024&textureFormat=png',
## url: `${window.location.origin}/gemini/static/6807f02bde708f2517e6f47a.glb`,  // SYNAP.SI
AVATAR_MODEL = os.path.join("static", "6807f02bde708f2517e6f47a.glb")
ENABLE_GOOGLE_CONNECTION = True

CONTEXT = ""

class NoCacheMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        response = await call_next(request)
        if request.url.path.startswith("/static"):
            response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
            response.headers["Pragma"] = "no-cache"
            response.headers["Expires"] = "0"
        return response


app = FastAPI()
app.add_middleware(NoCacheMiddleware)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)
app.mount("/static", StaticFiles(directory="static", html=True), name="static")


# ───── Strategia L: pool gRPC globale ─────
genai_client: genai.Client
structured_cache_name: str


class DummyGeminiHandler:
    def register_ws(self, client_id, ws): pass
    def unregister_ws(self, client_id): pass
    async def receive(self, data): pass
    last_seen = datetime.datetime.utcnow()
    async def aclose(self): pass


def genera_file_contesto(json_path, out_path):
    with open(json_path, encoding="utf-8") as f:
        categorie = json.load(f)

    corpo = []
    for chiave, dati in categorie.items():
        descrizione = dati.get("descrizione", "").strip()
        corpo.append(f"# {chiave}\n{descrizione}\n\n")

    # Esempi: meglio se sono conversazionali!
    esempi = ["---\nEsempi:\n"]
    for chiave, dati in categorie.items():
        domande = dati.get("domande", [])
        for domanda in domande:
            # Qui puoi aggiungere contesto finto o mini-conversazioni se vuoi aumentare la robustezza!
            esempi.append(
                f"Conversazione:\nUtente: {domanda}\nCategoria corretta: {chiave}\n\n"
            )

    full_prompt = "".join(corpo) + "".join(esempi)
    pathlib.Path(out_path).write_text(full_prompt, encoding="utf-8")
    print(f"File di contesto scritto in: {out_path}")




@app.on_event("startup")
async def init_genai_client():
    if ENABLE_GOOGLE_CONNECTION:
        global genai_client
        genai_client = genai.Client(
            api_key=GEMINI_API_KEY,
            http_options={"api_version": "v1beta"},
        )

        # 1. Genera dinamicamente il file di contesto
        genera_file_contesto("CONTESTO/categorie.json", "context.txt")

        # 2. Upload del file di contesto
        context_path = pathlib.Path("context.txt")
        context_file = genai_client.files.upload(file=context_path)
        
        ######################################################################################
        ####### VERY IMPORTANT ###############################################################
        # ,
        # "video": "",
        # "show_video": "",
        # "photo":
        ######################################################################################
        # 3) creazione della cache esplicita usando quel file
        # https://googleapis.github.io/python-genai/#caches

        # 3. Crea la cache strutturata per Gemini
        cache = genai_client.caches.create(
            model=GEMINI_model,  # modello aggiornato
            config=CreateCachedContentConfig(
                display_name="structured_context",
                system_instruction=Part.from_text(text="""
                Riceverai una parte della conversazione tra utente e agente. 
                Rispondi solo con la chiave della categoria più adatta all'ULTIMA domanda fatta dall'utente, tenendo conto del contesto della conversazione."""
                ),
                contents=[context_file],
                ttl="3600s",
            ),
        )

        ######################################################################################
        ####### VERY IMPORTANT ###############################################################
        ######################################################################################

        # # (facoltativo) verifica che la cache esista
        # print(f"Cache esplicita creata: {cache.name}, scade il {cache.expire_time}")
        # print(cache.name)

        gemini_module.structured_cache_name = cache.name

    else:
        print("1 - Connessione Google BLOCCATA per questa sessione. ")

        


@app.on_event("shutdown")
async def close_genai_client():
    await genai_client.aclose()


# ───── Strategia C + D: dependency e WebSocket ─────
async def get_gemini_handler() -> GeminiHandler:
    if ENABLE_GOOGLE_CONNECTION:
        session_id = str(uuid.uuid4())
        async with connect_gemini(genai_client, session_id=session_id) as handler:
            yield handler
        # FastAPI esegue automaticamente handler.aclose() e rimuove da active_handlers
    else:
        print("2 - Connessione Google BLOCCATA per questa sessione. ")
        # crea e restituisci Dummy, almeno per completare il ciclo di vita FastAPI
        yield DummyGeminiHandler()


@app.websocket("/client-audio")
async def ws_audio(ws: WebSocket, handler: GeminiHandler = Depends(get_gemini_handler)):  
    await ws.accept()
    client_id = str(uuid.uuid4())
    handler.register_ws(client_id, ws)

    try:
        while True:
            data = await ws.receive_bytes()
            #print(f"[ws_audio] ricevuti {len(data)} byte dal client")
            if handler in active_handlers.values():
                handler.last_seen = datetime.datetime.utcnow()
            await handler.receive((16000, data))
    except WebSocketDisconnect:
        handler.unregister_ws(client_id)
        for sid, h in list(active_handlers.items()):
            if h is handler:
                active_handlers.pop(sid, None)
        return




# ───── Strategia M: janitor per sessioni orfane ─────
JANITOR_TTL = datetime.timedelta(seconds=120)
JANITOR_SLEEP = 30

async def session_janitor():
    while True:
        now = datetime.datetime.utcnow()
        for sid, handler in list(active_handlers.items()):
            if (now - handler.last_seen) > JANITOR_TTL:
                try:
                    await handler.aclose()
                except Exception:
                    pass
                active_handlers.pop(sid, None)
        await asyncio.sleep(JANITOR_SLEEP)



@app.on_event("startup")
async def start_janitor():
    if ENABLE_GOOGLE_CONNECTION:
        asyncio.create_task(session_janitor())
    else:
        print("3 - Connessione Google BLOCCATA per questa sessione. ")


# ───── (Mantieni qui eventuali altri endpoint non-Gemini, se servono) ─────

@app.get("/status")
async def get_status():
    return {"status": "OK"}





@app.post("/api/tts")
async def proxy_tts(payload: dict = Body(...)):
    """
    Riceve la richiesta TTS dal client,
    la inoltra a Google usando la chiave sul server,
    e rimanda il risultato audio.
    """

    print('- ERROR 2: API TTS INVOCATA DAL CLIENT -')
    print('- ERROR 2: NON DOVREBBE ACCADERE -')
    headers = {"X-Goog-Api-Key": GEMINI_API_KEY}
    async with httpx.AsyncClient() as client:
        resp = await client.post(GOOGLE_TTS_URL, json=payload, headers=headers)
    return Response(content=resp.content, media_type=resp.headers.get("content-type"))





@app.get("/api/avatar-model")
async def avatar_model():
    """
    Serve il file .glb da un endpoint controllato.
    """
    return FileResponse(AVATAR_MODEL, media_type="model/gltf-binary")















