"""
candy.mix — Fusion de personnalités (v20)
==========================================

Combine plusieurs personnalités en une seule requête,
ou les enchaîne séquentiellement.

Usage :
    from candy import Mix, Coding, Security, Reviewer

    # Fusion : une seule requête avec expertise combinée
    result = Mix(Coding, Security).ask("Écris un serveur HTTP sécurisé en Python")
    print(result)

    # Chaîne : chaque personnalité travaille sur le résultat de la précédente
    result = Chain([Coding, Reviewer, Debugger]).run("Écris une API REST")
    print(result["final"])
    print(result["steps"])   # résultats intermédiaires

    # Async
    import asyncio
    result = asyncio.run(Mix(Coding, Security).ask_async("Écris un serveur sécurisé"))

    # Avec profil personnalisé
    result = Mix(Coding, Security).use("A").ask("...")
"""

import asyncio
from typing import Optional
from .client import _client
from .config import get_profile


class _MixInstance:
    """Instance Mix liée à un profil."""

    def __init__(self, personalities: list, profile_name: str = "default"):
        self._personalities = personalities
        self._profile_name  = profile_name

    def _build_system_hint(self) -> str:
        names = [p._personality for p in self._personalities]
        return (
            f"You are a fusion of {len(names)} expert AI personalities: "
            + ", ".join(names)
            + ". Combine all their expertise in your response."
        )

    def ask(self, prompt: str, context: Optional[str] = None) -> str:
        """Requête fusionnée — combine l'expertise de toutes les personnalités."""
        profile = get_profile(self._profile_name)
        # On utilise la première personnalité comme base, avec le hint de fusion
        primary = self._personalities[0]._personality
        combined_context = self._build_system_hint()
        if context:
            combined_context += f"\n\n--- Contexte fourni ---\n{context}"
        result = _client.ask(primary, prompt, profile, context=combined_context)
        return result.get("response", "")

    async def ask_async(self, prompt: str, context: Optional[str] = None) -> str:
        """Version async."""
        profile = get_profile(self._profile_name)
        primary = self._personalities[0]._personality
        combined_context = self._build_system_hint()
        if context:
            combined_context += f"\n\n--- Contexte fourni ---\n{context}"
        return await _client.ask_async(primary, prompt, profile, context=combined_context)

    def __repr__(self):
        names = [p._personality for p in self._personalities]
        return f"<Mix personalities={names} profile='{self._profile_name}'>"


class _MixFactory:
    """
    Crée une instance Mix à partir de plusieurs modules.

        Mix(Coding, Security).ask("...")
        Mix(Coding, Security).use("A").ask("...")
    """

    def __init__(self, *modules):
        if len(modules) < 2:
            raise ValueError("Mix nécessite au moins 2 personnalités.")
        self._modules = list(modules)

    def use(self, profile_name: str) -> _MixInstance:
        return _MixInstance(self._modules, profile_name)

    def ask(self, prompt: str, context: Optional[str] = None) -> str:
        return _MixInstance(self._modules).ask(prompt, context=context)

    async def ask_async(self, prompt: str, context: Optional[str] = None) -> str:
        return await _MixInstance(self._modules).ask_async(prompt, context=context)

    def __repr__(self):
        names = [m._personality for m in self._modules]
        return f"<Mix personalities={names}>"


def Mix(*modules) -> _MixFactory:
    """
    Fusionne plusieurs personnalités en une seule expertise combinée.

        from candy import Mix, Coding, Security
        result = Mix(Coding, Security).ask("Écris un serveur HTTP sécurisé")
    """
    return _MixFactory(*modules)


class Chain:
    """
    Enchaîne des personnalités séquentiellement.
    Chaque étape reçoit le résultat de la précédente comme contexte.

        from candy import Chain, Coding, Reviewer, Debugger
        result = Chain([Coding, Reviewer, Debugger]).run("Écris une API REST")
        print(result["final"])
        print(result["steps"])
    """

    def __init__(self, modules: list, profile: str = "default"):
        if len(modules) < 2:
            raise ValueError("Chain nécessite au moins 2 personnalités.")
        self._modules      = modules
        self._profile_name = profile

    def use(self, profile_name: str) -> "Chain":
        return Chain(self._modules, profile=profile_name)

    def run(self, prompt: str, verbose: bool = False) -> dict:
        """
        Exécute la chaîne synchroniquement.

        Retourne :
            {
                "final":   str,          # réponse de la dernière étape
                "steps":   [str, ...],   # réponses de chaque étape
                "modules": [str, ...]    # noms des personnalités utilisées
            }
        """
        profile    = get_profile(self._profile_name)
        steps      = []
        current    = prompt
        prev_result = ""

        for i, module in enumerate(self._modules):
            personality = module._personality
            context     = prev_result if prev_result else None
            if verbose:
                print(f"\033[90m[Chain] Étape {i+1}/{len(self._modules)} → {personality}\033[0m")
            result = _client.ask(personality, current, profile, context=context)
            response = result.get("response", "")
            steps.append(response)
            prev_result = response
            # À partir de l'étape 2, on passe la tâche originale + le résultat précédent
            if i == 0:
                current = f"Améliore et affine ce résultat :\n\n{response}\n\nTâche originale : {prompt}"

        return {
            "final":   steps[-1],
            "steps":   steps,
            "modules": [m._personality for m in self._modules],
        }

    async def run_async(self, prompt: str, verbose: bool = False) -> dict:
        """Version async de run()."""
        profile     = get_profile(self._profile_name)
        steps       = []
        current     = prompt
        prev_result = ""

        for i, module in enumerate(self._modules):
            personality = module._personality
            context     = prev_result if prev_result else None
            if verbose:
                print(f"\033[90m[Chain] Étape {i+1}/{len(self._modules)} → {personality}\033[0m")
            response = await _client.ask_async(personality, current, profile, context=context)
            steps.append(response)
            prev_result = response
            if i == 0:
                current = f"Améliore et affine ce résultat :\n\n{response}\n\nTâche originale : {prompt}"

        return {
            "final":   steps[-1],
            "steps":   steps,
            "modules": [m._personality for m in self._modules],
        }

    def __repr__(self):
        names = [m._personality for m in self._modules]
        return f"<Chain modules={names} profile='{self._profile_name}'>"
