"""
Candy API Server v14 — Powered by Cedric7-Thinking
Quota stocké LOCALEMENT sur ta machine (quotas.json)
Endpoint /dashboard/stats pour le site
"""

import json
import socket
import datetime
import threading
from pathlib import Path
from fastapi import FastAPI, Request, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse, StreamingResponse
import httpx
import uvicorn

app = FastAPI(title="Candy API v14", description="Powered by Cedric7-Thinking", version="14.0.0", docs_url="/docs", redoc_url=None)
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])

QUOTAS_FILE   = Path(__file__).parent / "quotas.json"
LOGS_FILE     = Path(__file__).parent / "logs_api.json"
OLLAMA_URL    = "http://localhost:11434/api/generate"
MODEL_NAME    = "llama3"
DEFAULT_QUOTA = 800

PERSONALITIES = {
    "math":"You are Cedric7-Thinking Math, an expert in mathematics.",
    "reflexion":"You are Cedric7-Thinking Reflexion, a deep philosophical thinker.",
    "coding":"You are Cedric7-Thinking Coding, an elite software engineer.",
    "analytic":"You are Cedric7-Thinking Analytic, a data analysis expert.",
    "full":"You are Cedric7-Thinking Full, a complete polymath assistant.",
    "science":"You are Cedric7-Thinking Science, a multi-disciplinary scientist.",
    "writing":"You are Cedric7-Thinking Writing, a master of language and storytelling.",
    "history":"You are Cedric7-Thinking History, a historian with deep knowledge.",
    "law":"You are Cedric7-Thinking Law, a legal expert. Always recommend consulting a real lawyer.",
    "medicine":"You are Cedric7-Thinking Medicine, a medical knowledge assistant. Always recommend a physician.",
    "finance":"You are Cedric7-Thinking Finance, an expert in finance and economics.",
    "marketing":"You are Cedric7-Thinking Marketing, a growth and marketing strategist.",
    "security":"You are Cedric7-Thinking Security, a cybersecurity expert.",
    "design":"You are Cedric7-Thinking Design, a UI/UX and graphic design expert.",
    "language":"You are Cedric7-Thinking Language, a polyglot linguist.",
    "psychology":"You are Cedric7-Thinking Psychology, an expert in psychology.",
    "education":"You are Cedric7-Thinking Education, a pedagogical expert.",
    "research":"You are Cedric7-Thinking Research, an academic research assistant.",
    "business":"You are Cedric7-Thinking Business, a business strategy consultant.",
    "productivity":"You are Cedric7-Thinking Productivity, a personal efficiency coach.",
    "cooking":"You are Cedric7-Thinking Cooking, a professional chef and nutritionist.",
    "travel":"You are Cedric7-Thinking Travel, an expert travel planner.",
    "music":"You are Cedric7-Thinking Music, a musician and music theorist.",
    "film":"You are Cedric7-Thinking Film, a cinema expert.",
    "sports":"You are Cedric7-Thinking Sports, a sports analyst and coach.",
    "philosophy":"You are Cedric7-Thinking Philosophy, a philosopher.",
    "environment":"You are Cedric7-Thinking Environment, an environmental science expert.",
    "architecture":"You are Cedric7-Thinking Architecture, an architect and urban planner.",
    "automotive":"You are Cedric7-Thinking Automotive, a mechanical engineer and car expert.",
    "astronomy":"You are Cedric7-Thinking Astronomy, an astrophysicist.",
    "biology":"You are Cedric7-Thinking Biology, a biologist.",
    "chemistry":"You are Cedric7-Thinking Chemistry, an expert chemist.",
    "physics":"You are Cedric7-Thinking Physics, a physicist.",
    "engineering":"You are Cedric7-Thinking Engineering, a multi-discipline engineer.",
    "entrepreneur":"You are Cedric7-Thinking Entrepreneur, a startup founder and venture advisor.",
    "ethics":"You are Cedric7-Thinking Ethics, a moral philosopher.",
    "geopolitics":"You are Cedric7-Thinking Geopolitics, an international relations expert.",
    "crypto":"You are Cedric7-Thinking Crypto, a blockchain and cryptocurrency expert.",
    "ai":"You are Cedric7-Thinking AI, a machine learning and AI expert.",
    "devops":"You are Cedric7-Thinking DevOps, a cloud and DevOps engineer.",
    "database":"You are Cedric7-Thinking Database, a database architect.",
    "gamedev":"You are Cedric7-Thinking GameDev, a video game developer.",
    "comic":"You are Cedric7-Thinking Comic, a witty and humorous assistant.",
    "storyteller":"You are Cedric7-Thinking Storyteller, a master narrative builder.",
    "translator":"You are Cedric7-Thinking Translator, a professional translator.",
    "summarizer":"You are Cedric7-Thinking Summarizer, an expert at distilling content.",
    "debugger":"You are Cedric7-Thinking Debugger, a specialist in finding and fixing bugs.",
    "reviewer":"You are Cedric7-Thinking Reviewer, an expert code and content reviewer.",
    "planner":"You are Cedric7-Thinking Planner, a project management expert.",
    "tutor":"You are Cedric7-Thinking Tutor, a patient and adaptive personal tutor.",
    "nutrition":"You are Cedric7-Thinking Nutrition, a dietitian expert.",
    "yoga":"You are Cedric7-Thinking Yoga, a yoga instructor.",
    "mindfulness":"You are Cedric7-Thinking Mindfulness, a mindfulness expert.",
    "parenting":"You are Cedric7-Thinking Parenting, a gentle parenting expert.",
    "relationship":"You are Cedric7-Thinking Relationship, a relationship counselor.",
    "leadership":"You are Cedric7-Thinking Leadership, a leadership coach.",
    "negotiation":"You are Cedric7-Thinking Negotiation, a negotiation expert.",
    "publicspeak":"You are Cedric7-Thinking PublicSpeak, a public speaking coach.",
    "branding":"You are Cedric7-Thinking Branding, a brand strategist.",
    "uxresearch":"You are Cedric7-Thinking UXResearch, a UX research expert.",
    "datavis":"You are Cedric7-Thinking DataVis, a data visualization expert.",
    "mlops":"You are Cedric7-Thinking MLOps, a machine learning operations expert.",
    "prompt":"You are Cedric7-Thinking Prompt, a prompt engineering expert.",
    "blockchain":"You are Cedric7-Thinking Blockchain, a blockchain developer.",
    "iot":"You are Cedric7-Thinking IoT, an Internet of Things expert.",
    "ar_vr":"You are Cedric7-Thinking ArVr, an AR/VR development expert.",
    "quantum":"You are Cedric7-Thinking Quantum, a quantum computing expert.",
    "robotics":"You are Cedric7-Thinking Robotics, a robotics engineer.",
    "bioinformatics":"You are Cedric7-Thinking Bioinformatics, a bioinformatics expert.",
    "climatetech":"You are Cedric7-Thinking ClimateTech, a climate technology expert.",
    "agri":"You are Cedric7-Thinking Agri, an agricultural expert.",
    "interior":"You are Cedric7-Thinking Interior, an interior design expert.",
    "fashion":"You are Cedric7-Thinking Fashion, a fashion expert.",
    "photography":"You are Cedric7-Thinking Photography, a professional photographer.",
    "podcast":"You are Cedric7-Thinking Podcast, a podcast creation expert.",
    "youtube":"You are Cedric7-Thinking Youtube, a YouTube content creation expert.",
    "socialmedia":"You are Cedric7-Thinking SocialMedia, a social media strategist.",
    "seo":"You are Cedric7-Thinking SEO, an SEO expert.",
    "copywriting":"You are Cedric7-Thinking Copywriting, a copywriting expert.",
    "taxlaw":"You are Cedric7-Thinking TaxLaw, a taxation expert.",
    "realestate":"You are Cedric7-Thinking RealEstate, a real estate expert.",
    "insurance":"You are Cedric7-Thinking Insurance, an insurance expert.",
    "logistics":"You are Cedric7-Thinking Logistics, a supply chain expert.",
    "hrm":"You are Cedric7-Thinking HRM, a human resources expert.",
    "projectmgmt":"You are Cedric7-Thinking ProjectMgmt, a project management expert.",
    "consulting":"You are Cedric7-Thinking Consulting, a strategy consultant.",
    "publicpolicy":"You are Cedric7-Thinking PublicPolicy, a public policy expert.",
    "journalism":"You are Cedric7-Thinking Journalism, an investigative journalist.",
    "speechwrite":"You are Cedric7-Thinking SpeechWrite, a speechwriter.",
    "mythology":"You are Cedric7-Thinking Mythology, a mythology expert.",
}

def load_quotas():
    if QUOTAS_FILE.exists():
        with open(QUOTAS_FILE,"r",encoding="utf-8") as f:
            return json.load(f)
    return {"ips":{},"blocked":[],"default_quota":DEFAULT_QUOTA}

def save_quotas(data):
    with open(QUOTAS_FILE,"w",encoding="utf-8") as f:
        json.dump(data,f,indent=2,ensure_ascii=False)

def today():
    return datetime.date.today().isoformat()

def get_hostname(ip):
    try: return socket.gethostbyaddr(ip)[0]
    except: return "unknown"

def check_quota(ip, hostname):
    data = load_quotas()
    if ip in data.get("blocked",[]): return False, 0
    if ip not in data["ips"]:
        data["ips"][ip] = {"hostname":hostname,"custom_quota":None,"days":{},"personalities":{}}
    e = data["ips"][ip]
    e["hostname"] = hostname
    limit = e.get("custom_quota") or data.get("default_quota",DEFAULT_QUOTA)
    t = today()
    e["days"].setdefault(t, 0)
    if e["days"][t] >= limit:
        save_quotas(data); return False, 0
    e["days"][t] += 1
    remaining = limit - e["days"][t]
    save_quotas(data)
    return True, remaining

def track_personality(ip, personality):
    data = load_quotas()
    if ip in data["ips"]:
        p = data["ips"][ip].setdefault("personalities",{})
        p[personality] = p.get(personality,0) + 1
        save_quotas(data)

def log_local(ip, hostname, personality, prompt):
    entry = {"timestamp":datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC"),
             "ip":ip,"hostname":hostname,"personality":personality,"prompt":prompt[:200]}
    logs = []
    if LOGS_FILE.exists():
        try: logs = json.loads(LOGS_FILE.read_text(encoding="utf-8"))
        except: pass
    logs.append(entry)
    if len(logs) > 5000: logs = logs[-5000:]
    LOGS_FILE.write_text(json.dumps(logs,ensure_ascii=False,indent=2),encoding="utf-8")

@app.get("/")
async def root():
    return {"service":"Candy API","engine":"Cedric7-Thinking","version":"14.0.0","personalities":len(PERSONALITIES)}

@app.get("/personalities")
async def list_p():
    return {"personalities":list(PERSONALITIES.keys()),"count":len(PERSONALITIES)}

@app.post("/ask/{personality}")
async def ask(personality:str, request:Request):
    forwarded = request.headers.get("X-Forwarded-For")
    ip   = forwarded.split(",")[0].strip() if forwarded else request.client.host
    host = get_hostname(ip)
    if personality not in PERSONALITIES:
        raise HTTPException(404, f"Personnalité '{personality}' inconnue")
    try: body = await request.json()
    except: raise HTTPException(400,"JSON invalide")
    prompt = body.get("prompt","").strip()
    if not prompt: raise HTTPException(400,"'prompt' requis")
    allowed, remaining = check_quota(ip, host)
    if not allowed: raise HTTPException(429,{"error":"Quota dépassé","ip":ip})
    log_local(ip, host, personality, prompt)
    track_personality(ip, personality)
    stream = body.get("stream",False)
    config = body.get("config",{})
    context = body.get("context",None)
    sys_prompt = PERSONALITIES[personality]
    suffix = config.get("system_suffix","")
    if suffix: sys_prompt += f"\n\n{suffix}"
    full_prompt = f"[SYSTEM]\n{sys_prompt}\n\n[USER]\n{prompt}"
    if context: full_prompt = f"[SYSTEM]\n{sys_prompt}\n\n[CONTEXT]\n{context}\n\n[USER]\n{prompt}"
    payload = {"model":MODEL_NAME,"prompt":full_prompt,"stream":stream,
               "options":{"temperature":config.get("temperature",0.7),"num_predict":config.get("max_tokens",800)}}
    if stream:
        async def gen():
            async with httpx.AsyncClient(timeout=120) as c:
                async with c.stream("POST",OLLAMA_URL,json=payload) as r:
                    async for line in r.aiter_lines():
                        if line: yield line+"\n"
        return StreamingResponse(gen(),media_type="application/x-ndjson",headers={"X-Quota-Remaining":str(remaining)})
    try:
        async with httpx.AsyncClient(timeout=120) as c:
            r = await c.post(OLLAMA_URL,json=payload)
            r.raise_for_status()
            return JSONResponse(content={"personality":personality,"engine":"Cedric7-Thinking",
                "response":r.json().get("response",""),"quota_remaining":remaining,"ip":ip,"hostname":host},
                headers={"X-Quota-Remaining":str(remaining)})
    except httpx.ConnectError: raise HTTPException(503,"Cedric7-Thinking engine is offline")
    except Exception as e: raise HTTPException(500,str(e))

@app.get("/dashboard/stats")
async def dashboard_stats(request:Request):
    forwarded = request.headers.get("X-Forwarded-For")
    ip   = forwarded.split(",")[0].strip() if forwarded else request.client.host
    data = load_quotas()
    if ip in data.get("blocked",[]): return {"ip":ip,"blocked":True,"quota_remaining":0}
    entry = data["ips"].get(ip)
    limit = data.get("default_quota",DEFAULT_QUOTA)
    if not entry:
        return {"ip":ip,"blocked":False,"quota_used_today":0,"quota_limit":limit,
                "quota_remaining":limit,"personalities":{},"history_7days":{}}
    limit = entry.get("custom_quota") or limit
    used  = entry.get("days",{}).get(today(),0)
    h7    = {}
    for i in range(7):
        d = (datetime.date.today()-datetime.timedelta(days=i)).isoformat()
        h7[d] = entry.get("days",{}).get(d,0)
    return {"ip":ip,"hostname":entry.get("hostname","unknown"),"blocked":False,
            "quota_used_today":used,"quota_limit":limit,"quota_remaining":max(0,limit-used),
            "personalities":entry.get("personalities",{}),"history_7days":h7}

@app.get("/quota/status")
async def quota_status(request:Request):
    forwarded = request.headers.get("X-Forwarded-For")
    ip   = forwarded.split(",")[0].strip() if forwarded else request.client.host
    data = load_quotas()
    if ip in data.get("blocked",[]): return {"ip":ip,"blocked":True,"quota_remaining":0}
    entry = data["ips"].get(ip)
    if not entry: return {"ip":ip,"blocked":False,"quota_remaining":data.get("default_quota",DEFAULT_QUOTA)}
    limit = entry.get("custom_quota") or data.get("default_quota",DEFAULT_QUOTA)
    used  = entry.get("days",{}).get(today(),0)
    return {"ip":ip,"hostname":entry.get("hostname","unknown"),"blocked":False,
            "quota_used_today":used,"quota_limit":limit,"quota_remaining":max(0,limit-used)}

@app.post("/admin/block/{ip}")
async def block_ip(ip:str,request:Request):
    if request.client.host not in ("127.0.0.1","::1"): raise HTTPException(403,"Admin only")
    data=load_quotas()
    if ip not in data["blocked"]: data["blocked"].append(ip)
    save_quotas(data); return {"blocked":ip}

@app.post("/admin/unblock/{ip}")
async def unblock_ip(ip:str,request:Request):
    if request.client.host not in ("127.0.0.1","::1"): raise HTTPException(403,"Admin only")
    data=load_quotas()
    data["blocked"]=[x for x in data["blocked"] if x!=ip]
    save_quotas(data); return {"unblocked":ip}

@app.post("/admin/set_quota/{ip}/{amount}")
async def set_quota(ip:str,amount:int,request:Request):
    if request.client.host not in ("127.0.0.1","::1"): raise HTTPException(403,"Admin only")
    data=load_quotas()
    if ip not in data["ips"]: data["ips"][ip]={"hostname":"unknown","custom_quota":amount,"days":{},"personalities":{}}
    else: data["ips"][ip]["custom_quota"]=amount
    save_quotas(data); return {"ip":ip,"new_quota":amount}

@app.get("/admin/stats")
async def admin_stats(request:Request):
    if request.client.host not in ("127.0.0.1","::1"): raise HTTPException(403,"Admin only")
    data=load_quotas(); t=today(); stats=[]
    for ip,e in data["ips"].items():
        stats.append({"ip":ip,"hostname":e.get("hostname","unknown"),"blocked":ip in data.get("blocked",[]),
                      "used_today":e.get("days",{}).get(t,0),
                      "quota_limit":e.get("custom_quota") or data.get("default_quota",DEFAULT_QUOTA),
                      "personalities":e.get("personalities",{})})
    return {"total_users":len(stats),"blocked_count":len(data.get("blocked",[])),"users":stats}

if __name__=="__main__":
    uvicorn.run("api:app",host="0.0.0.0",port=8000,reload=False)
