"""
candy.stream_file — Streaming vers fichier (v21)
=================================================

Génère du contenu long token par token et l'écrit directement dans un fichier
sans saturer la mémoire. Idéal pour les romans, rapports, code volumineux.

Usage :
    from candy import Writing, Coding

    # Streaming vers fichier — Writing
    Writing.stream_to_file(
        "roman.txt",
        "Écris un roman de science-fiction d'environ 5000 mots sur une IA qui rêve",
        show_progress=True   # affiche une barre de progression dans le terminal
    )

    # Avec profil
    from candy import cfg
    cfg.A.lang = "FR"
    cfg.A.max_tokens = 4096

    Writing.use("A").stream_to_file("article.md", "Écris un article complet sur le RAG")

    # Append mode — ajoute à un fichier existant
    Writing.stream_to_file("journal.txt", "Entrée du jour...", append=True)

    # Callback par chunk
    def on_chunk(token: str, total_chars: int):
        if total_chars % 500 == 0:
            print(f"  {total_chars} caractères générés...")

    Writing.stream_to_file("output.txt", "...", on_chunk=on_chunk)
"""

import sys
import time
from pathlib import Path
from typing import Optional, Callable


def stream_to_file(
    client,
    personality: str,
    profile,
    path: str,
    prompt: str,
    *,
    context:       Optional[str]      = None,
    append:        bool               = False,
    show_progress: bool               = True,
    on_chunk:      Optional[Callable] = None,
    encoding:      str                = "utf-8",
) -> dict:
    """
    Streame la réponse directement dans un fichier.

    Retourne :
        {
            "path":       str,    # chemin du fichier
            "chars":      int,    # nombre de caractères écrits
            "tokens":     int,    # estimation du nombre de tokens
            "elapsed_s":  float,  # durée en secondes
        }
    """
    mode = "a" if append else "w"
    path_obj = Path(path)
    path_obj.parent.mkdir(parents=True, exist_ok=True)

    t0          = time.time()
    total_chars = 0
    total_tokens = 0

    if show_progress:
        print(f"\033[90m[candy] 📝 Streaming → {path} ({'append' if append else 'write'})\033[0m")

    gen = client.ask(personality, prompt, profile,
                     context=context, stream=True)

    with open(path_obj, mode, encoding=encoding) as f:
        for token in gen:
            f.write(token)
            total_chars  += len(token)
            total_tokens += 1

            if on_chunk:
                try:
                    on_chunk(token, total_chars)
                except Exception:
                    pass

            if show_progress and total_chars % 200 == 0:
                elapsed = time.time() - t0
                sys.stdout.write(
                    f"\r\033[90m[candy] ✍  {total_chars:,} chars · {elapsed:.1f}s\033[0m   "
                )
                sys.stdout.flush()

    elapsed = time.time() - t0
    if show_progress:
        print(f"\r\033[92m[candy] ✓ {total_chars:,} chars → {path}  ({elapsed:.1f}s)\033[0m   ")

    return {
        "path":      str(path_obj),
        "chars":     total_chars,
        "tokens":    total_tokens,
        "elapsed_s": round(elapsed, 2),
    }
