"""
candy.compare — Comparer plusieurs personnalités (v21)
=======================================================

Pose la même question à plusieurs personnalités en parallèle
et retourne les réponses côte à côte.

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

    # Comparer 3 personnalités sur le même prompt
    result = Compare(Coding, Security, Reviewer).ask("Comment sécuriser une API REST ?")

    print(result["coding"])    # réponse de Coding
    print(result["security"])  # réponse de Security
    print(result["reviewer"])  # réponse de Reviewer
    result.show()              # affichage formaté dans le terminal

    # Async — toutes les réponses en parallèle
    import asyncio
    result = asyncio.run(Compare(Coding, Security).ask_async("..."))

    # Avec profil
    result = Compare(Coding, Security).use("A").ask("...")

    # Candy.compare() — shortcut direct
    from candy import Candy
    result = Candy.compare("Comment optimiser une requête SQL ?",
                           personalities=["coding", "database", "analytic"])
"""

import asyncio
from typing import Optional


class _CompareResult(dict):
    """
    Résultat d'une comparaison. Hérite de dict pour un accès direct.
    result["coding"] == result.coding
    """

    def show(self, truncate: int = 400):
        """Affiche les réponses côte à côte dans le terminal."""
        cyan  = "\033[96m"
        white = "\033[97m"
        gray  = "\033[90m"
        reset = "\033[0m"
        sep   = "─" * 60

        print(f"\n{gray}{sep}{reset}")
        print(f"{cyan}🍬 Compare — {len(self)} personnalités{reset}")
        print(f"{gray}{sep}{reset}\n")

        for personality, response in self.items():
            if personality == "_prompt":
                continue
            print(f"{white}▶ {personality.upper()}{reset}")
            text = response if isinstance(response, str) else str(response)
            if len(text) > truncate:
                text = text[:truncate] + f"\n{gray}[... tronqué]{reset}"
            print(text)
            print(f"\n{gray}{sep}{reset}\n")

    def best(self, criterion: str = "longest") -> tuple:
        """
        Retourne (personality_name, response) selon un critère.
        criterion: "longest" | "shortest"
        """
        items = [(k, v) for k, v in self.items() if k != "_prompt" and isinstance(v, str)]
        if criterion == "longest":
            return max(items, key=lambda x: len(x[1]))
        elif criterion == "shortest":
            return min(items, key=lambda x: len(x[1]))
        return items[0]

    def to_markdown(self) -> str:
        """Exporte la comparaison en Markdown."""
        lines = [f"# Compare — {self.get('_prompt', '')[:80]}\n"]
        for personality, response in self.items():
            if personality == "_prompt":
                continue
            lines.append(f"## {personality.upper()}\n")
            lines.append(str(response))
            lines.append("\n---\n")
        return "\n".join(lines)


class _CompareInstance:
    def __init__(self, modules: list, profile_name: str = "default"):
        self._modules      = modules
        self._profile_name = profile_name

    def ask(self, prompt: str, context: Optional[str] = None) -> _CompareResult:
        """Requêtes synchrones (séquentielles)."""
        from .client import _client
        from .config import get_profile

        profile = get_profile(self._profile_name)
        result  = _CompareResult({"_prompt": prompt})

        for module in self._modules:
            personality = module._personality
            try:
                r = _client.ask(personality, prompt, profile, context=context)
                result[personality] = r.get("response", "")
            except Exception as e:
                result[personality] = f"[ERREUR] {e}"

        return result

    async def ask_async(self, prompt: str, context: Optional[str] = None) -> _CompareResult:
        """Requêtes async — toutes en parallèle."""
        from .client import _client
        from .config import get_profile

        profile = get_profile(self._profile_name)

        async def _fetch(module):
            try:
                return module._personality, await _client.ask_async(
                    module._personality, prompt, profile, context=context
                )
            except Exception as e:
                return module._personality, f"[ERREUR] {e}"

        responses = await asyncio.gather(*[_fetch(m) for m in self._modules])
        result    = _CompareResult({"_prompt": prompt})
        for personality, response in responses:
            result[personality] = response
        return result

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


class _CompareFactory:
    """
    from candy import Compare, Coding, Security, Reviewer
    result = Compare(Coding, Security, Reviewer).ask("Comment sécuriser une API ?")
    """

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

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

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

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

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


def Compare(*modules) -> _CompareFactory:
    """
    Compare plusieurs personnalités sur le même prompt.

        from candy import Compare, Coding, Security
        result = Compare(Coding, Security).ask("Comment sécuriser une API ?")
        result.show()
        print(result["coding"])
    """
    return _CompareFactory(*modules)
