"""
candy.helper — Agent IA qui écrit du code candy pour toi
=========================================================

Helper connaît toute la syntaxe candy v6 et génère du code
prêt à être copié-collé et exécuté.

UTILISATION :
    from candy import Helper

    code = Helper.write("un livre de 20 pages sur la Révolution française")
    print(code)

    # Ou directement exécuter le code généré :
    Helper.run("un script qui résume un texte en 3 langues différentes")

    # Avec plus de détails :
    code = Helper.write(
        "un chatbot en boucle qui répond à des questions de maths",
        profile="A"
    )
"""

import httpx
import json
from typing import Optional

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

# ── Documentation injectée dans Helper ──────────────────────────────────────
_CANDY_DOCS = """
Tu es Helper, un expert en candy-ai v6. Tu génères du code Python candy parfait.

=== CANDY v6 — SYNTAXE COMPLÈTE ===

--- IMPORT ---
from candy import cfg, Helper
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, Conversation
)

--- PROFILS ---
from candy import cfg

# Créer un profil manuellement
cfg.A.lang           = "FR"       # langue OBLIGATOIRE à définir
cfg.A.max_tokens     = 2000       # 100=court 600=moyen 1024=std 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 0.7=équilibré 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

# Utiliser un preset
cfg.A = cfg.preset("french_beginner")   # FR, détaillé, encourageant, débutant
cfg.B = cfg.preset("french_expert")     # FR, concis, strict, expert
cfg.C = cfg.preset("english_beginner")  # EN, détaillé, encourageant, débutant
cfg.D = cfg.preset("english_expert")    # EN, technique, strict, expert
cfg.E = cfg.preset("coder")             # technique, markdown, expert, 3000 tokens
cfg.F = cfg.preset("academic")          # académique, markdown, expert, 2000 tokens
cfg.G = cfg.preset("creative")          # décontracté, créatif, 1500 tokens
cfg.H = cfg.preset("teacher")           # pédagogue, markdown, débutant, 2000 tokens
cfg.I = cfg.preset("quick")             # très court, 120 tokens
cfg.J = cfg.preset("journalist")        # journalistique, markdown, 2000 tokens
cfg.K = cfg.preset("storyteller")       # narratif, créatif, 4096 tokens
cfg.L = cfg.preset("analyst")           # analytique, bullet, markdown, expert
cfg.M = cfg.preset("coach")             # coach, encourageant, 1500 tokens
cfg.N = cfg.preset("debug")             # débogage, technique, markdown, 3000 tokens

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

--- APPEL SIMPLE ---
# Sans profil → utilise default
response = Coding.ask("Ta question")
print(response)

# Avec profil nommé
response = Math.use("A").ask("Ta question")
print(response)

# Avec contexte (données, code, texte...)
response = Analytic.use("A").ask("Analyse ceci", context="données ici")

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

# stream_print() — streame ET affiche directement
Writing.use("A").stream_print("Écris une histoire")

--- BATCH (plusieurs prompts d'un coup) ---
questions = ["Question 1", "Question 2", "Question 3"]
reponses = Coding.use("A").batch(questions)
for q, r in zip(questions, reponses):
    print(f"Q: {q}\\nR: {r}\\n")

--- CONVERSATION MULTI-TOURS ---
# Crée une session qui mémorise l'historique
session = Coding.chat(profile="A")
print(session.say("Première question"))
print(session.say("Question suivante — le modèle se souvient du contexte"))
session.show_history()      # affiche l'historique
session.save("chat.json")   # sauvegarde
session.load("chat.json")   # charge
session.clear()             # efface
print(session.count_turns()) # nb de tours

# Ou via Conversation directement
from candy import Conversation
session = Conversation(Coding, profile="A", max_history=20)

--- UTILITAIRES ---
print(Coding.is_online())     # True/False
print(Coding.ping())          # latence ms
print(Coding.quota())         # quota restant
print(Coding.personalities()) # liste des 50 personnalités
data = Coding.use("A").ask_with_meta("Hello")  # réponse + métadonnées
print(data["response"])
print(data["quota_remaining"])

--- HELPER (génère du code candy) ---
from candy import Helper
code = Helper.write("ce que tu veux faire")
print(code)

=== RÈGLES IMPORTANTES ===
1. TOUJOURS définir cfg.X.lang = "FR" (ou autre) pour garantir la langue
2. TOUJOURS utiliser print() pour afficher les réponses
3. Pour les textes longs (livres, articles), utiliser max_tokens = 4096
4. Pour les livres : générer d'abord le plan avec Planner, puis écrire chapitre par chapitre avec Writing
5. Sauvegarder les longs textes dans un fichier .md ou .txt
6. Toujours importer cfg en premier
7. La langue doit être définie AVANT tout appel .ask()

=== FIN DOCUMENTATION ===
"""

_HELPER_SYSTEM = f"""
Tu es Helper, l'agent de candy-ai v6.
Ton unique rôle est de générer du code Python candy parfait, complet et prêt à être exécuté.

{_CANDY_DOCS}

RÈGLES DE GÉNÉRATION :
- Retourne UNIQUEMENT du code Python, rien d'autre
- Pas d'explication avant ou après le code
- Pas de balises markdown (pas de ```python)
- Le code doit être complet, fonctionnel, et inclure tous les imports
- Toujours définir les profils avec cfg avant d'utiliser .ask()
- Toujours utiliser print() pour afficher les résultats
- Ajouter des commentaires courts pour structurer le code
- Pour les tâches longues (livres, analyses), faire une boucle chapitre par chapitre
- Toujours sauvegarder les longs outputs dans un fichier
"""


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


class _Helper:
    """
    Agent candy qui génère du code Python candy prêt à l'emploi.

    Usage :
        from candy import Helper

        code = Helper.write("un livre de 20 pages sur l'espace")
        print(code)

        Helper.run("un script qui traduit un texte en 5 langues")
    """

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

        Args:
            task:    Description de ce que tu veux faire en français ou anglais.
            profile: Nom du profil candy à utiliser dans le code généré (optionnel).

        Returns:
            Code Python candy prêt à être copié-collé et exécuté.

        Exemple :
            code = Helper.write("un livre de 20 pages sur la Révolution française")
            print(code)
        """
        prompt = f"Génère du code Python candy v6 pour accomplir cette tâche : {task}"
        if profile:
            prompt += f"\nUtilise le profil '{profile}' dans le code généré."

        try:
            base = _get_api_base()
            payload = {
                "prompt": f"[INSTRUCTION: Réponds UNIQUEMENT avec du code Python, aucun texte avant ou après, aucune balise markdown.]\n\n{prompt}",
                "stream": False,
                "config": {
                    "lang":           "EN",
                    "max_tokens":     2000,
                    "temperature":    0.3,
                    "system_suffix":  _HELPER_SYSTEM,
                    "style":          "technical",
                    "output_format":  "text",
                    "expertise":      "expert",
                    "safe_mode":      True,
                    "include_examples": False,
                    "tone":           "neutral",
                }
            }
            resp = httpx.post(f"{base}/ask/coding", json=payload, timeout=60)
            resp.raise_for_status()
            data = resp.json()
            code = data.get("response", "").strip()
            # Nettoie les éventuelles balises markdown si le modèle en met quand même
            code = code.replace("```python", "").replace("```", "").strip()
            return code
        except Exception as e:
            return f"# Erreur Helper : {e}"

    def run(self, task: str, profile: Optional[str] = None):
        """
        Génère le code candy et l'exécute directement.

        ⚠️ Le code généré est exécuté dans l'environnement courant.
        Vérifie le code avant de l'utiliser en production.

        Args:
            task:    Description de ce que tu veux faire.
            profile: Nom du profil à utiliser.
        """
        code = self.write(task, profile=profile)
        print("\033[90m[Helper] Code généré :\033[0m")
        print("\033[96m" + code + "\033[0m")
        print("\033[90m[Helper] Exécution...\033[0m\n")
        exec(code, {"__name__": "__main__"})

    def explain(self, task: str) -> str:
        """
        Génère le code ET une explication de ce qu'il fait.

        Args:
            task: Description de la tâche.

        Returns:
            Code Python + explication.
        """
        code = self.write(task)
        try:
            base = _get_api_base()
            payload = {
                "prompt": f"[INSTRUCTION: Tu dois répondre en français.]\n\nExplique en 3-4 phrases ce que fait ce code candy :\n\n{code}",
                "stream": False,
                "config": {
                    "lang": "FR", "max_tokens": 300, "temperature": 0.5,
                    "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/coding", json=payload, timeout=30)
            resp.raise_for_status()
            explanation = resp.json().get("response", "").strip()
            return f"# Code :\n{code}\n\n# Explication :\n# {explanation.replace(chr(10), chr(10)+'# ')}"
        except Exception as e:
            return code

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


# ── Instance globale ─────────────────────────────────────────────────────────
Helper = _Helper()
