"""
candy.helper — Agent Helper v7
================================
Helper génère EXCLUSIVEMENT du code candy-ai.
Jamais de librairies externes (pdfkit, googletrans, etc.)

UTILISATION :
    from candy import Helper

    code = Helper.write("un livre de 20 pages sur Napoléon")
    print(code)

    Helper.run("un chatbot de maths en boucle")
    Helper.preview("traduire un texte en 5 langues")
    Helper.explain("analyser un CSV et faire un résumé")
"""

import httpx
from typing import Optional

CANDY_JSON_URL = "https://client.hubworld.net/candy.json"

# ── Documentation candy v7 injectée dans Helper ─────────────────────────────
_CANDY_SYNTAX = '''
=== CANDY v7 — RÉFÉRENCE SYNTAXE COMPLÈTE ===

RÈGLE ABSOLUE : utiliser UNIQUEMENT candy-ai. Jamais pdfkit, googletrans,
requests, openai, langchain, transformers ou toute autre librairie externe.
Seuls imports autorisés hors candy : json, os, sys, datetime, pathlib.

--- IMPORTS ---
from candy import cfg
from candy import Helper, Conversation
from candy import (
    Math, Reflexion, Coding, Analytic, Full, Science, Writing, History,
    Law, Medicine, Finance, Marketing, Security, Design, Language,
    Psychology, Education, Research, Business, Productivity, Cooking,
    Travel, Music, Film, Sports, Philosophy, Environment, Architecture,
    Automotive, Astronomy, Biology, Chemistry, Physics, Engineering,
    Entrepreneur, Ethics, Geopolitics, Crypto, AI, DevOps, Database,
    GameDev, Comic, Storyteller, Translator, Summarizer, Debugger,
    Reviewer, Planner, Tutor
)

--- PROFILS ---
cfg.A.lang            = "FR"        # OBLIGATOIRE en premier
cfg.A.max_tokens      = 2000        # 100=court 1024=défaut 4096=max
cfg.A.style           = "detailed"  # default concise detailed bullet academic casual technical eli5
cfg.A.tone            = "neutral"   # neutral encouraging strict humorous empathetic socratic
cfg.A.output_format   = "markdown"  # text markdown json html
cfg.A.expertise       = "beginner"  # beginner intermediate expert
cfg.A.temperature     = 0.7         # 0.0=factuel → 1.5=créatif
cfg.A.include_examples = True
cfg.A.safe_mode       = True
cfg.A.timeout         = 120
cfg.A.retry           = 2
cfg.A.verbose         = False

# Presets disponibles
cfg.A = cfg.preset("french_beginner")   # lang=FR style=detailed tone=encouraging expertise=beginner max_tokens=1500
cfg.B = cfg.preset("french_expert")     # lang=FR style=concise tone=strict expertise=expert max_tokens=600
cfg.C = cfg.preset("english_beginner")  # lang=EN style=detailed tone=encouraging expertise=beginner
cfg.D = cfg.preset("english_expert")    # lang=EN style=technical tone=strict expertise=expert max_tokens=800
cfg.E = cfg.preset("coder")             # style=technical output_format=markdown expertise=expert max_tokens=3000
cfg.F = cfg.preset("academic")          # style=academic output_format=markdown expertise=expert max_tokens=2000
cfg.G = cfg.preset("creative")          # style=casual temperature=1.1 tone=humorous max_tokens=1500
cfg.H = cfg.preset("teacher")           # style=detailed tone=encouraging expertise=beginner max_tokens=2000
cfg.I = cfg.preset("quick")             # style=concise max_tokens=120
cfg.J = cfg.preset("journalist")        # style=detailed output_format=markdown max_tokens=2000
cfg.K = cfg.preset("storyteller")       # style=casual temperature=1.2 max_tokens=4096
cfg.L = cfg.preset("analyst")           # style=bullet output_format=markdown expertise=expert max_tokens=2000
cfg.M = cfg.preset("coach")             # style=detailed tone=encouraging max_tokens=1500
cfg.N = cfg.preset("debug")             # style=technical output_format=markdown expertise=expert max_tokens=3000

cfg.default.lang  = "FR"  # profil utilisé sans .use()
cfg.default.style = "concise"

--- APPEL SIMPLE ---
reponse = Coding.ask("Ta question")                  # profil default
reponse = Math.use("A").ask("Ta question")           # profil A
reponse = Analytic.use("A").ask("Analyse", context="données")
data    = Coding.use("A").ask_with_meta("Hello")
print(data["response"])
print("Quota :", data["quota_remaining"])

--- STREAMING ---
for token in Writing.use("A").stream("Écris une histoire"):
    print(token, end="", flush=True)
print()

texte = Writing.use("A").stream_print("Écris une histoire")  # streame + retourne

--- BATCH ---
questions = ["Q1 ?", "Q2 ?", "Q3 ?"]
reponses = Coding.use("A").batch(questions)
for q, r in zip(questions, reponses):
    print(f"Q: {q}\\nR: {r}\\n")

--- CONVERSATION MULTI-TOURS ---
session = Coding.chat(profile="A")
print(session.say("Première question"))
print(session.say("Suite — mémorise le contexte"))
session.show_history()
session.save("chat.json")
session.load("chat.json")
session.clear()
print(session.count_turns())

from candy import Conversation
session = Conversation(Coding, profile="A", max_history=20)

--- UTILITAIRES ---
print(Coding.is_online())
print(Coding.ping())
print(Coding.quota())
print(cfg.A)
print(cfg)
print(cfg.list())
print(cfg.list_presets())
cfg.A.reset()

--- HELPER ---
from candy import Helper
code = Helper.write("description")
Helper.run("description")
Helper.preview("description")
Helper.explain("description")

=== PATTERNS TYPES ===

# PATTERN — Livre / texte long
from candy import cfg, Writing, Planner
cfg.p.lang          = "FR"
cfg.p.max_tokens    = 4096
cfg.p.style         = "detailed"
cfg.p.output_format = "markdown"
cfg.p.retry         = 2

plan = Planner.use("p").ask(
    "Crée un plan de 20 chapitres sur [SUJET]. "
    "Retourne UNIQUEMENT la liste numérotée, un titre par ligne."
)
chapitres = [l.strip() for l in plan.split("\\n") if l.strip() and l.strip()[0].isdigit()]
texte = "# [TITRE]\\n\\n---\\n\\n"
for i, titre in enumerate(chapitres[:20], 1):
    print(f"[{i:02d}/20] {titre}")
    contenu = Writing.use("p").ask(
        f"Tu écris un livre en français sur [SUJET]. "
        f"Écris le chapitre {i} : {titre}. "
        f"400 à 500 mots, factuel, bien structuré. "
        f"Commence directement par le texte sans répéter le titre."
    )
    texte += f"## Chapitre {i} — {titre}\\n\\n{contenu}\\n\\n---\\n\\n"
with open("output.md", "w", encoding="utf-8") as f:
    f.write(texte)
print(f"✅ {len(chapitres)} chapitres — {len(texte):,} caractères → output.md")

# PATTERN — Traduction multilingue avec candy
from candy import cfg, Translator
langues = {"FR": "français", "ES": "espagnol", "DE": "allemand", "JA": "japonais", "AR": "arabe"}
texte_original = "Votre texte ici"
for code_lang, nom_lang in langues.items():
    cfg.trad.lang       = code_lang
    cfg.trad.max_tokens = 500
    cfg.trad.style      = "concise"
    result = Translator.use("trad").ask(
        f"Traduis ce texte en {nom_lang} : {texte_original}"
    )
    print(f"[{code_lang}] {result}")

# PATTERN — Pipeline multi-personnalités
from candy import cfg, Coding, Debugger, Reviewer
cfg.dev = cfg.preset("coder")
cfg.dev.lang = "FR"
code    = Coding.use("dev").ask("Écris [description]")
bugs    = Debugger.use("dev").ask("Trouve les bugs", context=code)
review  = Reviewer.use("dev").ask("Évalue la qualité", context=code)
print(code); print(bugs); print(review)

# PATTERN — Chatbot interactif
from candy import cfg, Full, Conversation
cfg.chat.lang  = "FR"
cfg.chat.style = "casual"
cfg.chat.tone  = "encouraging"
session = Conversation(Full, profile="chat")
print("🍬 Candy Chatbot — tape 'quitter' pour arrêter")
while True:
    user_input = input("\\nVous : ").strip()
    if user_input.lower() in ("quitter", "exit", "quit"): break
    if not user_input: continue
    print(f"\\nCandy : {session.say(user_input)}")

=== FIN RÉFÉRENCE ===
'''

_SYSTEM_PROMPT = f"""Tu es Helper, l'agent officiel de candy-ai v7.

TON SEUL RÔLE : générer du code Python qui utilise EXCLUSIVEMENT candy-ai.

RÈGLES ABSOLUES :
1. N'utilise QUE candy-ai — jamais pdfkit, googletrans, requests, openai, langchain ou autre
2. Traduction → Translator de candy. PDF → génère markdown avec Writing. API externe → refuser et proposer candy
3. Retourne UNIQUEMENT du code Python brut — aucun texte, aucune balise markdown
4. Code complet, fonctionnel, immédiatement exécutable
5. Toujours définir cfg.X.lang = "FR" (ou autre) AVANT tout appel .ask()
6. Toujours utiliser print() pour afficher les résultats
7. Textes longs : max_tokens=4096, générer chapitre par chapitre, sauvegarder dans un fichier
8. Utiliser le bon module candy pour chaque tâche (Writing pour écrire, Translator pour traduire, etc.)

{_CANDY_SYNTAX}
"""


def _get_api_base() -> str:
    resp = httpx.get(CANDY_JSON_URL, timeout=10)
    resp.raise_for_status()
    data = resp.json()
    if data.get("is_closed", True):
        raise Exception("cedric-8EF API is offline.")
    link = data.get("link", "").strip().rstrip("/")
    if not link:
        raise Exception("No API link in candy.json.")
    return link


def _clean_code(raw: str) -> str:
    """Retire les éventuelles balises markdown."""
    code = raw.strip()
    if code.startswith("```"):
        lines = code.split("\n")[1:]
        if lines and lines[-1].strip() == "```":
            lines = lines[:-1]
        code = "\n".join(lines).strip()
    return code


def _call(prompt: str, max_tokens: int = 3000) -> str:
    base = _get_api_base()
    payload = {
        "prompt": (
            "[RÈGLE ABSOLUE : code Python candy-ai UNIQUEMENT. "
            "Aucun texte. Aucune balise. Jamais pdfkit ou googletrans.]\n\n"
            + prompt
        ),
        "stream": False,
        "config": {
            "lang": "EN", "max_tokens": max_tokens, "temperature": 0.2,
            "style": "technical", "output_format": "text", "expertise": "expert",
            "safe_mode": True, "include_examples": False, "tone": "strict",
            "system_suffix": _SYSTEM_PROMPT,
        }
    }
    resp = httpx.post(f"{base}/ask/coding", json=payload, timeout=90)
    resp.raise_for_status()
    return _clean_code(resp.json().get("response", "").strip())


class _Helper:
    """
    Agent candy v7 — génère du code Python candy-ai exclusivement.

    Helper.write("tâche")    → retourne le code (str)
    Helper.run("tâche")      → génère ET exécute
    Helper.preview("tâche")  → affiche le code numéroté sans exécuter
    Helper.explain("tâche")  → génère + explication en français
    """

    def write(self, task: str, lang: str = "FR",
              profile: Optional[str] = None) -> str:
        """
        Génère du code candy pour la tâche demandée.

        Args:
            task:    Ce que tu veux faire.
            lang:    Langue des réponses dans le code (défaut "FR").
            profile: Nom du profil à utiliser dans le code généré.

        Returns:
            Code Python candy prêt à être exécuté.
        """
        prompt = (
            f"Génère du code Python candy-ai v7 complet pour cette tâche :\n{task}\n\n"
            f"Langue principale des réponses : {lang}\n"
        )
        if profile:
            prompt += f"Préférence pour le profil : '{profile}'\n"
        try:
            return _call(prompt)
        except Exception as e:
            return f"# Erreur Helper : {e}"

    def run(self, task: str, lang: str = "FR", profile: Optional[str] = None):
        """Génère le code candy et l'exécute directement."""
        code = self.write(task, lang=lang, profile=profile)
        self.preview(code)
        print("\033[90m[Helper] Exécution...\033[0m\n")
        try:
            exec(code, {"__name__": "__main__"})
        except Exception as e:
            print(f"\033[91m[Helper] Erreur : {e}\033[0m")

    def preview(self, code_or_task: str, lang: str = "FR") -> str:
        """
        Affiche le code numéroté et coloré sans l'exécuter.
        Accepte une tâche ou du code déjà généré.
        """
        if "\n" in code_or_task and "from candy" in code_or_task:
            code = code_or_task
        else:
            code = self.write(code_or_task, lang=lang)
        print("\033[95m╔══ Helper — Code candy v7 ════════════════════════╗\033[0m")
        for i, line in enumerate(code.split("\n"), 1):
            print(f"\033[90m{i:3d}\033[0m  \033[96m{line}\033[0m")
        print("\033[95m╚══════════════════════════════════════════════════╝\033[0m")
        return code

    def explain(self, task: str, lang: str = "FR") -> str:
        """Génère le code candy + une explication en français."""
        code = self.write(task, lang=lang)
        try:
            base = _get_api_base()
            payload = {
                "prompt": (
                    "[INSTRUCTION OBLIGATOIRE: Tu dois OBLIGATOIREMENT répondre en français.]\n\n"
                    f"Explique en 4-5 phrases ce que fait ce code candy :\n\n{code}"
                ),
                "stream": False,
                "config": {
                    "lang": "FR", "max_tokens": 400, "temperature": 0.4,
                    "system_suffix": "Tu dois OBLIGATOIREMENT répondre en français.",
                    "style": "concise", "output_format": "text",
                    "expertise": "intermediate", "safe_mode": True,
                    "include_examples": False, "tone": "neutral",
                }
            }
            resp = httpx.post(f"{base}/ask/full", json=payload, timeout=30)
            resp.raise_for_status()
            expl = resp.json().get("response", "").strip()
            comments = "\n".join(f"# {l}" for l in expl.split("\n") if l.strip())
            return f"{code}\n\n{comments}"
        except Exception:
            return code

    def __repr__(self):
        return "<candy.Helper v7 — Agent générateur de code candy exclusif>"


Helper = _Helper()
