"""
candy.utils — Utilitaires v12
==============================
20 nouvelles fonctionnalités disponibles via :
    from candy import utils
    from candy.utils import translate, summarize, detect_lang, ...
"""

import json
import time
import hashlib
import re
from typing import Optional
from .client import _client
from .config import get_profile, _Config


# ── 1. Détection automatique de langue ────────────────────────────────────────
def detect_lang(text: str) -> str:
    """
    Détecte la langue d'un texte.
    Retourne le code langue : "FR", "EN", "ES", etc.

    Exemple::
        from candy.utils import detect_lang
        print(detect_lang("Bonjour tout le monde"))  # → FR
    """
    profile = get_profile("default")
    result = _client.ask("language", 
        f"Detect the language of this text and return ONLY the 2-letter ISO code (FR, EN, ES, DE, IT, etc.). Text: {text[:200]}",
        profile, stream=False)
    return result.get("response", "EN").strip().upper()[:2]


# ── 2. Traduction rapide ──────────────────────────────────────────────────────
def translate(text: str, to: str = "EN", from_lang: str = "auto") -> str:
    """
    Traduit un texte vers la langue cible.

    Args:
        text:      Texte à traduire.
        to:        Code langue cible ("FR", "EN", "ES", "DE"...).
        from_lang: Code langue source ("auto" pour détection automatique).

    Exemple::
        from candy.utils import translate
        print(translate("Hello world", to="FR"))  # → Bonjour le monde
    """
    from .config import get_profile
    profile = get_profile("default")
    # Force la langue de réponse
    import copy
    p = copy.copy(profile)
    object.__setattr__(p, "lang", to)
    object.__setattr__(p, "max_tokens", 500)

    if from_lang == "auto":
        prompt = f"Translate to {to}, return ONLY the translation: {text}"
    else:
        prompt = f"Translate from {from_lang} to {to}, return ONLY the translation: {text}"

    result = _client.ask("translator", prompt, p, stream=False)
    return result.get("response", "").strip()


# ── 3. Résumé en N mots ────────────────────────────────────────────────────────
def summarize(text: str, words: int = 50, lang: str = "FR") -> str:
    """
    Résume un texte en N mots maximum.

    Args:
        text:  Texte à résumer.
        words: Nombre maximum de mots.
        lang:  Langue du résumé.

    Exemple::
        from candy.utils import summarize
        print(summarize(long_text, words=30, lang="FR"))
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", words * 3)
    result = _client.ask("summarizer",
        f"Summarize in maximum {words} words. Return ONLY the summary: {text}",
        p, stream=False)
    return result.get("response", "").strip()


# ── 4. Correction orthographique ──────────────────────────────────────────────
def spellcheck(text: str, lang: str = "FR") -> str:
    """
    Corrige les fautes d'orthographe et de grammaire.
    Retourne le texte corrigé sans explication.

    Exemple::
        from candy.utils import spellcheck
        print(spellcheck("Bonjour je mappelle Jean"))
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", len(text) * 2 + 50)
    result = _client.ask("reviewer",
        f"Fix all spelling and grammar errors. Return ONLY the corrected text, nothing else: {text}",
        p, stream=False)
    return result.get("response", "").strip()


# ── 5. Extraction de mots-clés ────────────────────────────────────────────────
def keywords(text: str, n: int = 5, lang: str = "FR") -> list:
    """
    Extrait les N mots-clés principaux d'un texte.
    Retourne une liste de strings.

    Exemple::
        from candy.utils import keywords
        kws = keywords("Article sur le machine learning et les réseaux neuronaux", n=3)
        # → ["machine learning", "réseaux neuronaux", "intelligence artificielle"]
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 200)
    result = _client.ask("analytic",
        f"Extract the {n} most important keywords from this text. "
        f"Return ONLY a JSON array of strings, nothing else. Example: [\"word1\",\"word2\"]. Text: {text[:500]}",
        p, stream=False)
    raw = result.get("response", "[]").strip()
    try:
        raw = re.search(r'\[.*\]', raw, re.DOTALL)
        return json.loads(raw.group()) if raw else []
    except Exception:
        return [w.strip() for w in raw.split(",")][:n]


# ── 6. Analyse de sentiment ───────────────────────────────────────────────────
def sentiment(text: str) -> dict:
    """
    Analyse le sentiment d'un texte.
    Retourne {"label": "positif"|"négatif"|"neutre", "score": 0.0-1.0, "explanation": "..."}

    Exemple::
        from candy.utils import sentiment
        s = sentiment("Ce produit est absolument fantastique !")
        print(s["label"])  # → positif
    """
    profile = get_profile("default")
    import copy
    p = copy.copy(profile)
    object.__setattr__(p, "lang", "EN")
    object.__setattr__(p, "max_tokens", 100)
    result = _client.ask("analytic",
        f'Analyze the sentiment of this text. Return ONLY a JSON object with keys "label" (positive/negative/neutral), "score" (0.0 to 1.0), "explanation" (one sentence). Text: {text[:300]}',
        p, stream=False)
    raw = result.get("response", "{}").strip()
    try:
        m = re.search(r'\{.*\}', raw, re.DOTALL)
        return json.loads(m.group()) if m else {"label": "neutral", "score": 0.5, "explanation": raw}
    except Exception:
        return {"label": "neutral", "score": 0.5, "explanation": raw}


# ── 7. Génération de titre ────────────────────────────────────────────────────
def generate_title(text: str, lang: str = "FR", n: int = 3) -> list:
    """
    Génère N titres accrocheurs pour un texte.

    Exemple::
        from candy.utils import generate_title
        titles = generate_title(my_article, lang="FR", n=5)
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 200)
    result = _client.ask("writing",
        f"Generate {n} catchy titles for this content. Return ONLY a JSON array of strings. Content: {text[:500]}",
        p, stream=False)
    raw = result.get("response", "[]").strip()
    try:
        m = re.search(r'\[.*\]', raw, re.DOTALL)
        return json.loads(m.group()) if m else [raw]
    except Exception:
        return [line.strip("- •123456789.").strip() for line in raw.split("\n") if line.strip()][:n]


# ── 8. Q&A sur un texte ──────────────────────────────────────────────────────
def ask_about(text: str, question: str, lang: str = "FR") -> str:
    """
    Pose une question sur un texte donné en contexte.

    Exemple::
        from candy.utils import ask_about
        answer = ask_about(article, "Quelle est la conclusion principale ?")
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 500)
    result = _client.ask("full", question, p, context=text, stream=False)
    return result.get("response", "").strip()


# ── 9. Reformulation ─────────────────────────────────────────────────────────
def rephrase(text: str, style: str = "formal", lang: str = "FR") -> str:
    """
    Reformule un texte dans un style différent.

    Args:
        style: "formal", "casual", "simple", "professional", "creative"

    Exemple::
        from candy.utils import rephrase
        print(rephrase("C'est trop bien", style="formal"))
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", len(text) * 3 + 100)
    result = _client.ask("writing",
        f"Rephrase this text in a {style} style in {lang}. Return ONLY the rephrased text: {text}",
        p, stream=False)
    return result.get("response", "").strip()


# ── 10. Génération de FAQ ─────────────────────────────────────────────────────
def generate_faq(text: str, n: int = 5, lang: str = "FR") -> list:
    """
    Génère une FAQ à partir d'un texte.
    Retourne une liste de {"question": ..., "answer": ...}

    Exemple::
        from candy.utils import generate_faq
        faq = generate_faq(product_description, n=3)
        for item in faq:
            print(f"Q: {item['question']}")
            print(f"A: {item['answer']}")
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 800)
    result = _client.ask("education",
        f"Generate {n} FAQ questions and answers based on this content. "
        f"Return ONLY a JSON array of objects with 'question' and 'answer' keys. Content: {text[:800]}",
        p, stream=False)
    raw = result.get("response", "[]").strip()
    try:
        m = re.search(r'\[.*\]', raw, re.DOTALL)
        return json.loads(m.group()) if m else []
    except Exception:
        return []


# ── 11. Comparaison de textes ─────────────────────────────────────────────────
def compare(text1: str, text2: str, lang: str = "FR") -> dict:
    """
    Compare deux textes et retourne leurs similitudes et différences.
    Retourne {"similarities": [...], "differences": [...], "conclusion": "..."}

    Exemple::
        from candy.utils import compare
        result = compare(version1, version2)
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 600)
    result = _client.ask("analytic",
        f"Compare these two texts. Return ONLY a JSON object with keys: "
        f"'similarities' (array), 'differences' (array), 'conclusion' (string).\n"
        f"TEXT 1: {text1[:400]}\nTEXT 2: {text2[:400]}",
        p, stream=False)
    raw = result.get("response", "{}").strip()
    try:
        m = re.search(r'\{.*\}', raw, re.DOTALL)
        return json.loads(m.group()) if m else {"similarities": [], "differences": [], "conclusion": raw}
    except Exception:
        return {"similarities": [], "differences": [], "conclusion": raw}


# ── 12. Génération de code ────────────────────────────────────────────────────
def codegen(description: str, language: str = "python") -> str:
    """
    Génère du code à partir d'une description.

    Args:
        description: Ce que le code doit faire.
        language:    Langage de programmation cible.

    Exemple::
        from candy.utils import codegen
        code = codegen("une fonction qui trie une liste par longueur", language="python")
        print(code)
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", "EN")
    object.__setattr__(p, "max_tokens", 1500)
    object.__setattr__(p, "output_format", "text")
    result = _client.ask("coding",
        f"Write {language} code for: {description}. "
        f"Return ONLY the code, no explanation, no markdown backticks.",
        p, stream=False)
    code = result.get("response", "").strip()
    return code.replace("```" + language, "").replace("```", "").strip()


# ── 13. Explication de code ───────────────────────────────────────────────────
def explain_code(code: str, lang: str = "FR") -> str:
    """
    Explique ce que fait un bout de code en langage naturel.

    Exemple::
        from candy.utils import explain_code
        print(explain_code("def fib(n): return n if n<=1 else fib(n-1)+fib(n-2)"))
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 400)
    result = _client.ask("coding",
        f"Explain what this code does in simple terms. Be concise: {code}",
        p, stream=False)
    return result.get("response", "").strip()


# ── 14. Génération de données de test ────────────────────────────────────────
def mock_data(schema: str, n: int = 5, lang: str = "EN") -> list:
    """
    Génère N exemples de données fictives selon un schéma.

    Args:
        schema: Description du schéma (ex: "user with name, email, age")
        n:      Nombre d'exemples.

    Exemple::
        from candy.utils import mock_data
        users = mock_data("user with name, email, age, city", n=3)
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 800)
    result = _client.ask("coding",
        f"Generate {n} realistic mock data examples for: {schema}. "
        f"Return ONLY a JSON array of objects, nothing else.",
        p, stream=False)
    raw = result.get("response", "[]").strip()
    try:
        m = re.search(r'\[.*\]', raw, re.DOTALL)
        return json.loads(m.group()) if m else []
    except Exception:
        return []


# ── 15. Timer de réponse ─────────────────────────────────────────────────────
def timed_ask(module_name: str, prompt: str, profile_name: str = "default") -> dict:
    """
    Envoie un prompt et mesure le temps de réponse.
    Retourne {"response": ..., "time_ms": ..., "words": ...}

    Exemple::
        from candy.utils import timed_ask
        result = timed_ask("coding", "What is a decorator?")
        print(f"Réponse en {result['time_ms']}ms — {result['words']} mots")
    """
    from . import modules
    profile = get_profile(profile_name)
    t0 = time.time()
    result = _client.ask(module_name, prompt, profile, stream=False)
    elapsed = round((time.time() - t0) * 1000, 1)
    response = result.get("response", "")
    return {
        "response": response,
        "time_ms":  elapsed,
        "words":    len(response.split()),
        "chars":    len(response),
    }


# ── 16. Cache de réponses ─────────────────────────────────────────────────────
_CACHE: dict = {}

def cached_ask(module_name: str, prompt: str, profile_name: str = "default",
               ttl: int = 3600) -> str:
    """
    Comme .ask() mais avec un cache en mémoire (évite les doublons).

    Args:
        ttl: Durée de vie du cache en secondes (défaut 1h).

    Exemple::
        from candy.utils import cached_ask
        # Premier appel → API
        r1 = cached_ask("math", "C'est quoi pi ?")
        # Deuxième appel → cache instantané
        r2 = cached_ask("math", "C'est quoi pi ?")
    """
    key = hashlib.md5(f"{module_name}:{profile_name}:{prompt}".encode()).hexdigest()
    now = time.time()
    if key in _CACHE:
        entry = _CACHE[key]
        if now - entry["ts"] < ttl:
            return entry["response"]
    profile = get_profile(profile_name)
    result = _client.ask(module_name, prompt, profile, stream=False)
    response = result.get("response", "")
    _CACHE[key] = {"response": response, "ts": now}
    return response

def clear_cache():
    """Vide le cache de réponses."""
    _CACHE.clear()


# ── 17. Génération de prompt optimisé ────────────────────────────────────────
def optimize_prompt(raw_prompt: str, personality: str = "full", lang: str = "FR") -> str:
    """
    Prend un prompt brut et le reformule de façon optimale pour candy.

    Exemple::
        from candy.utils import optimize_prompt
        better = optimize_prompt("explique moi les décorateurs", personality="coding")
        print(better)
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 300)
    result = _client.ask("full",
        f"Rewrite this prompt to be clearer, more precise and effective for a {personality} AI assistant. "
        f"Return ONLY the improved prompt: {raw_prompt}",
        p, stream=False)
    return result.get("response", raw_prompt).strip()


# ── 18. Extraction de données structurées ────────────────────────────────────
def extract(text: str, fields: list, lang: str = "EN") -> dict:
    """
    Extrait des champs spécifiques d'un texte non structuré.

    Args:
        text:   Texte source.
        fields: Liste des champs à extraire (ex: ["nom", "email", "date"]).

    Exemple::
        from candy.utils import extract
        data = extract(email_text, ["sender_name", "subject", "date", "action_required"])
        print(data["sender_name"])
    """
    import copy
    profile = get_profile("default")
    p = copy.copy(profile)
    object.__setattr__(p, "lang", lang)
    object.__setattr__(p, "max_tokens", 400)
    fields_str = ", ".join(f'"{f}"' for f in fields)
    result = _client.ask("analytic",
        f"Extract these fields from the text: [{fields_str}]. "
        f"Return ONLY a JSON object with these keys. Use null if not found. Text: {text[:600]}",
        p, stream=False)
    raw = result.get("response", "{}").strip()
    try:
        m = re.search(r'\{.*\}', raw, re.DOTALL)
        return json.loads(m.group()) if m else {f: None for f in fields}
    except Exception:
        return {f: None for f in fields}


# ── 19. Pipeline personnalisé ─────────────────────────────────────────────────
def pipeline(text: str, steps: list, lang: str = "FR") -> dict:
    """
    Applique une séquence de transformations sur un texte.
    Chaque étape est un tuple (module, instruction).

    Exemple::
        from candy.utils import pipeline
        result = pipeline(
            "Mon texte brut ici...",
            steps=[
                ("summarizer", "Résume en 2 phrases"),
                ("writing",    "Rends le plus engageant"),
                ("reviewer",   "Vérifie la grammaire"),
            ],
            lang="FR"
        )
        for step, output in result["steps"].items():
            print(f"{step}: {output}")
        print("Final:", result["final"])
    """
    import copy
    current = text
    results = {}
    for i, (module, instruction) in enumerate(steps):
        profile = get_profile("default")
        p = copy.copy(profile)
        object.__setattr__(p, "lang", lang)
        object.__setattr__(p, "max_tokens", 800)
        result = _client.ask(module,
            f"{instruction}. Return ONLY the result: {current}",
            p, stream=False)
        current = result.get("response", current).strip()
        results[f"step_{i+1}_{module}"] = current
    return {"steps": results, "final": current}


# ── 20. Rapport complet sur un texte ─────────────────────────────────────────
def full_report(text: str, lang: str = "FR") -> dict:
    """
    Génère un rapport complet sur un texte :
    résumé, sentiment, mots-clés, langue détectée, titres suggérés.

    Exemple::
        from candy.utils import full_report
        report = full_report(my_article, lang="FR")
        print(report["summary"])
        print(report["sentiment"])
        print(report["keywords"])
    """
    return {
        "detected_lang": detect_lang(text),
        "summary":       summarize(text, words=60, lang=lang),
        "sentiment":     sentiment(text),
        "keywords":      keywords(text, n=5, lang=lang),
        "titles":        generate_title(text, lang=lang, n=3),
    }
