# ============================================================
#  projet.py — Lecture et gestion du projet
#  rolax1212 v1.0 by Roland
# ============================================================

import re
import os
import subprocess
import tempfile
import sys
from pathlib import Path
from .ui import console, ok, info, warn, err, code, separateur

# ============================================================
#  CONFIG
# ============================================================
EXTENSIONS_CODE = {
    ".py":   "python",
    ".php":  "php",
    ".java": "java",
    ".js":   "javascript",
    ".ts":   "typescript",
    ".html": "html",
    ".css":  "css",
    ".sql":  "sql",
    ".sh":   "bash",
    ".md":   "markdown",
    ".json": "json",
    ".xml":  "xml",
    ".c":    "c",
    ".cpp":  "cpp",
    ".h":    "c",
    ".txt":  "text",
    ".env":  "text",
    ".yaml": "yaml",
    ".yml":  "yaml",
    ".toml": "toml",
    ".ini":  "ini",
}

IGNORER = {
    "node_modules", ".git", "__pycache__", "venv", ".venv",
    "dist", "build", ".idea", ".vscode", "vendor", "target",
    ".next", ".nuxt", "coverage", ".pytest_cache"
}

MAX_FICHIER  = 8000
MAX_PROJET   = 40000


# ============================================================
#  LECTURE PROJET
# ============================================================
def lire_projet(path_str):
    """Lit tous les fichiers du projet. Retourne (Path, dict fichiers)."""
    path = Path(path_str).resolve()
    if not path.exists():
        err(f"Chemin introuvable : {path}")
        return None, {}

    fichiers     = {}
    total_chars  = 0
    tronque      = False

    tous = sorted(path.rglob("*"), key=lambda p: (
        0 if p.suffix in [".md", ".json", ".env", ".txt"] else 1
    ))

    for f in tous:
        if not f.is_file():
            continue
        if any(ign in f.parts for ign in IGNORER):
            continue
        if f.suffix not in EXTENSIONS_CODE:
            continue

        try:
            contenu = f.read_text(encoding="utf-8", errors="ignore")
            if not contenu.strip():
                continue

            if len(contenu) > MAX_FICHIER:
                contenu = contenu[:MAX_FICHIER] + f"\n... [TRONQUÉ — {len(contenu)} chars]"

            rel = str(f.relative_to(path))
            fichiers[rel]  = contenu
            total_chars   += len(contenu)

            if total_chars > MAX_PROJET:
                tronque = True
                break
        except Exception:
            continue

    if tronque:
        warn("Projet volumineux — contexte partiel chargé.")

    return path, fichiers


def construire_contexte(fichiers):
    """Construit le contexte textuel pour le LLM."""
    if not fichiers:
        return "Projet vide — aucun fichier de code trouvé."

    ctx = "STRUCTURE DU PROJET :\n"
    for f in fichiers:
        ctx += f"  - {f}\n"
    ctx += "\n"

    for chemin, contenu in fichiers.items():
        lang = EXTENSIONS_CODE.get(Path(chemin).suffix, "")
        ctx += f"\n{'='*48}\nFICHIER : {chemin}\n{'='*48}\n"
        ctx += f"```{lang}\n{contenu}\n```\n"

    return ctx


def afficher_structure(path, fichiers):
    """Affiche l'arborescence dans le terminal."""
    from rich.table import Table
    t = Table(title=f"Projet : {path}", style="cyan")
    t.add_column("Fichier",  style="white")
    t.add_column("Langage",  style="yellow")
    t.add_column("Lignes",   justify="right", style="green")

    for chemin, contenu in fichiers.items():
        lang = EXTENSIONS_CODE.get(Path(chemin).suffix, "?")
        nb   = len(contenu.splitlines())
        t.add_row(chemin, lang, str(nb))

    console.print(t)


# ============================================================
#  EXTRACTION BLOCS CODE
# ============================================================
def extraire_blocs(reponse):
    """Extrait les blocs ```lang ... ``` avec nom de fichier associé."""
    pattern = r"```(\w*)\n(.*?)```"
    blocs   = re.findall(pattern, reponse, re.DOTALL)
    parties = re.split(r"```\w*\n.*?```", reponse, flags=re.DOTALL)

    resultats = []
    for i, (lang, contenu) in enumerate(blocs):
        if not contenu.strip():
            continue

        nom            = None
        contexte_avant = parties[i] if i < len(parties) else ""
        dernieres      = "\n".join(contexte_avant.strip().splitlines()[-6:])

        patterns_nom = [
            r"FICHIER\s*:\s*([^\s\n`]+\.\w+)",
            r"fichier\s*`([^`]+\.\w+)`",
            r"`([a-zA-Z0-9_\-/]+\.\w+)`",
            r"\*\*([a-zA-Z0-9_\-/]+\.\w+)\*\*",
            r"###?\s+([a-zA-Z0-9_\-/]+\.\w+)",
            r"(?:créer?|crée?|générer?|fichier)\s+([a-zA-Z0-9_\-/]+\.\w+)",
            r"([a-zA-Z0-9_\-/]+\.(?:php|py|js|java|html|css|sql|ts|sh|md))\s*:",
        ]

        for pat in patterns_nom:
            m = re.search(pat, dernieres, re.IGNORECASE)
            if m:
                nom = m.group(1).strip()
                break
        if not nom:
            for pat in patterns_nom:
                m = re.search(pat, contexte_avant, re.IGNORECASE)
                if m:
                    nom = m.group(1).strip()
                    break

        resultats.append({"lang": lang or "text", "code": contenu.strip(), "nom": nom})

    return resultats


def nom_par_defaut(lang, index=1):
    """Nom de fichier par défaut selon le langage."""
    ext_map = {
        "python": ".py", "php": ".php", "java": ".java",
        "javascript": ".js", "typescript": ".ts",
        "html": ".html", "css": ".css", "sql": ".sql",
        "bash": ".sh", "markdown": ".md"
    }
    return f"fichier_{index}{ext_map.get(lang, '.txt')}"


def sauvegarder(chemin, contenu):
    """Sauvegarde un fichier en créant les dossiers si besoin."""
    p = Path(chemin)
    p.parent.mkdir(parents=True, exist_ok=True)
    p.write_text(contenu, encoding="utf-8")
    ok(f"Fichier créé : {chemin}")


# ============================================================
#  EXECUTION
# ============================================================
def executer(chemin, cwd=None):
    """Exécute un fichier et retourne (code_retour, stdout, stderr)."""
    p   = Path(chemin)
    cwd = cwd or p.parent

    cmds = {
        ".py":  [sys.executable, str(p)],
        ".sh":  ["bash", str(p)],
        ".php": ["php", str(p)],
        ".js":  ["node", str(p)],
    }

    cmd = cmds.get(p.suffix)
    if not cmd:
        return None, "", f"Extension {p.suffix} non exécutable automatiquement."

    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=30, cwd=str(cwd))
        return r.returncode, r.stdout.strip(), r.stderr.strip()
    except subprocess.TimeoutExpired:
        return -1, "", "Timeout 30s"
    except FileNotFoundError as e:
        return -1, "", f"Interpréteur non trouvé : {e}"
    except Exception as e:
        return -1, "", str(e)


# ============================================================
#  TASK.MD
# ============================================================
def lire_task(path):
    """Cherche Task.md dans le projet."""
    for nom in ["Task.md", "task.md", "TASK.md", "tasks.md", "TODO.md"]:
        p = Path(path) / nom
        if p.exists():
            return p, p.read_text(encoding="utf-8")
    return None, None


def extraire_taches(contenu):
    """Extrait les tâches d'un Task.md."""
    taches = []
    for ligne in contenu.splitlines():
        l = ligne.strip()
        if l.startswith("- [ ]") or l.startswith("- [x]"):
            t = l.replace("- [ ]", "").replace("- [x]", "").strip()
            if t:
                taches.append(t)
        elif l.startswith("- ") or l.startswith("* "):
            t = l[2:].strip()
            if t and not t.startswith("#") and len(t) > 3:
                taches.append(t)
    return taches


def marquer_tache_faite(path_task, tache):
    """Marque une tâche comme faite dans Task.md."""
    try:
        contenu = Path(path_task).read_text(encoding="utf-8")
        nouveau = contenu.replace(f"- [ ] {tache}", f"- [x] {tache}", 1)
        Path(path_task).write_text(nouveau, encoding="utf-8")
    except Exception:
        pass
