# -*- coding: utf-8 -*-
"""
Composition du prompt et préparation d'un projet.

C'est ici que le gabarit `prompt-conception-site-web.md` devient une requête :
le formulaire remplit `gabarits/mission.md`, les fichiers déposés sont rangés
à leur emplacement définitif dans le site, et les PDF partent en pièces
jointes — l'API Claude les lit nativement, il n'y a donc rien à extraire.
"""

import base64
import mimetypes
import os
import shutil

import config
import projets


# Pièces jointes : types que l'API sait lire.
TYPES_IMAGE = {
    ".png": "image/png",
    ".jpg": "image/jpeg",
    ".jpeg": "image/jpeg",
    ".gif": "image/gif",
    ".webp": "image/webp",
}
EXTENSIONS_POLICE = {".woff2", ".woff", ".ttf", ".otf"}
EXTENSIONS_VECTEUR = {".svg"}

# Au-delà, on se contente de citer les visuels : les joindre tous coûterait
# cher en jetons sans aider davantage.
MAX_IMAGES_JOINTES = int(os.environ.get("PLATEFORME_MAX_IMAGES", "6"))

# L'API plafonne une requête à 32 Mo. On garde de la marge.
MAX_OCTETS_JOINTS = 24 * 1024 * 1024

CHAMPS = [
    "client", "secteur", "nature", "objectif", "public", "slogan", "domaine",
    "coordonnees", "reseaux", "date_lieu", "couleurs", "police_titres",
    "police_textes", "variantes_logo", "arborescence", "contenus",
    "references", "remarques", "pages",
]


def _gabarit(nom):
    with open(os.path.join(config.DOSSIER_GABARITS, nom), encoding="utf-8") as f:
        return f.read()


def _valeur(brief, cle):
    valeur = (brief.get(cle) or "").strip()
    return valeur if valeur else "_non précisé_"


def ranger_entrees(projet_id):
    """Copie les fichiers déposés à leur emplacement définitif dans le site.

    Le modèle ne peut pas écrire de binaire : les visuels et les polices sont
    donc mis en place par la plateforme, et leur chemin exact lui est donné.
    Renvoie (inventaire_texte, pdfs, images_jointes).
    """
    entrees = projets.dossier_entrees(projet_id)
    site = projets.dossier_site(projet_id)
    range_ = {"logos": [], "photos": [], "polices": [], "documents": [], "autres": []}

    for nom in sorted(os.listdir(entrees)):
        source = os.path.join(entrees, nom)
        if not os.path.isfile(source):
            continue
        extension = os.path.splitext(nom)[1].lower()
        minuscule = nom.lower()

        if extension in TYPES_IMAGE or extension in EXTENSIONS_VECTEUR:
            categorie = "logos" if ("logo" in minuscule or "favicon" in minuscule) else "photos"
            destination = os.path.join(site, "assets", "img", categorie, nom)
        elif extension in EXTENSIONS_POLICE:
            categorie = "polices"
            destination = os.path.join(site, "assets", "fonts", nom)
        elif extension == ".pdf":
            range_["documents"].append((nom, source))
            continue
        else:
            range_["autres"].append((nom, source))
            continue

        os.makedirs(os.path.dirname(destination), exist_ok=True)
        shutil.copy2(source, destination)
        range_[categorie].append((os.path.relpath(destination, site), source))

    lignes = []
    etiquettes = [
        ("logos", "Logos"),
        ("photos", "Photos et visuels"),
        ("polices", "Polices"),
    ]
    for cle, etiquette in etiquettes:
        if range_[cle]:
            lignes.append("**%s**" % etiquette)
            for chemin, source in range_[cle]:
                lignes.append("- `%s` (%d Ko)" % (chemin, os.path.getsize(source) // 1024))
            lignes.append("")
    if range_["documents"]:
        lignes.append("**Documents joints à ce message** (lis-les directement)")
        for nom, source in range_["documents"]:
            lignes.append("- %s (%d Ko)" % (nom, os.path.getsize(source) // 1024))
        lignes.append("")
    if range_["autres"]:
        lignes.append("**Fichiers non exploitables automatiquement**")
        for nom, _ in range_["autres"]:
            lignes.append("- %s" % nom)
        lignes.append("")

    inventaire = "\n".join(lignes).strip() or "_Aucun fichier déposé._"
    images = [s for _, s in range_["logos"] + range_["photos"]
              if os.path.splitext(s)[1].lower() in TYPES_IMAGE]
    return inventaire, [s for _, s in range_["documents"]], images


def _bloc_document(chemin):
    with open(chemin, "rb") as f:
        donnees = base64.standard_b64encode(f.read()).decode("ascii")
    return {
        "type": "document",
        "source": {"type": "base64", "media_type": "application/pdf", "data": donnees},
        "title": os.path.basename(chemin),
    }


def _bloc_image(chemin):
    extension = os.path.splitext(chemin)[1].lower()
    with open(chemin, "rb") as f:
        donnees = base64.standard_b64encode(f.read()).decode("ascii")
    return {
        "type": "image",
        "source": {
            "type": "base64",
            "media_type": TYPES_IMAGE[extension],
            "data": donnees,
        },
    }


def composer(projet_id, brief):
    """Fabrique les blocs du premier message : pièces jointes puis brief.

    Les pièces jointes viennent en tête : elles ne changent jamais, et forment
    donc le préfixe stable que le cache relira à chaque tour.
    """
    inventaire, pdfs, images = ranger_entrees(projet_id)
    blocs = []
    octets = 0
    ignores = []

    for chemin in pdfs:
        taille = os.path.getsize(chemin)
        if octets + taille > MAX_OCTETS_JOINTS:
            ignores.append(os.path.basename(chemin))
            continue
        blocs.append(_bloc_document(chemin))
        octets += taille

    for chemin in images[:MAX_IMAGES_JOINTES]:
        taille = os.path.getsize(chemin)
        if octets + taille > MAX_OCTETS_JOINTS:
            ignores.append(os.path.basename(chemin))
            continue
        blocs.append({"type": "text", "text": "Visuel : %s" % os.path.basename(chemin)})
        blocs.append(_bloc_image(chemin))
        octets += taille

    valeurs = {cle: _valeur(brief, cle) for cle in CHAMPS}
    valeurs["inventaire"] = inventaire
    valeurs["consigne_charte"] = (
        "La charte graphique est jointe en PDF. Lis-la et relèves-en toi-même "
        "les couleurs exactes, les polices et les usages du logo ; le tableau "
        "ci-dessus n'est qu'une indication, le PDF fait foi. Annonce en début "
        "de travail la charte que tu as retenue."
        if pdfs else
        "Aucune charte n'est jointe : applique les couleurs et les polices du "
        "tableau ci-dessus, et signale dans le README ce qui reste à confirmer."
    )
    texte = _gabarit("mission.md").format(**valeurs)

    libre = (brief.get("brief_libre") or "").strip()
    if libre:
        texte += "\n\n# Brief complémentaire\n\n" + libre
    if ignores:
        texte += (
            "\n\n_Pièces trop volumineuses pour être jointes : %s. "
            "Appuie-toi sur le brief._" % ", ".join(ignores)
        )

    blocs.append({"type": "text", "text": texte})

    # Point de reprise du cache sur le dernier bloc du brief : tout ce qui
    # précède (pièces jointes comprises) sera relu en cache à chaque tour.
    # Durée longue, car une génération s'étale sur une trentaine de minutes.
    blocs[-1]["cache_control"] = {"type": "ephemeral", "ttl": "1h"}
    return blocs


def preparer(projet_id, brief):
    """Construit conversation.json et met le projet dans la file."""
    blocs = composer(projet_id, brief)
    conversation = {
        "modele": config.MODELE,
        "systeme": [{
            "type": "text",
            "text": _gabarit("systeme.md"),
            "cache_control": {"type": "ephemeral", "ttl": "1h"},
        }],
        "messages": [{"role": "user", "content": blocs}],
        "relances": 0,
        "brief": brief,
    }
    projets.ecrire_conversation(projet_id, conversation)
    projets.journaliser(projet_id, "prepare", "%d bloc(s) dans le brief" % len(blocs))
    projets.changer_etat(projet_id, projets.EN_ATTENTE)
    return conversation
