# -*- coding: utf-8 -*-
"""Génère une arborescence Starlight (Astro) à partir des docs Markdown de docs/.

Sortie : astro-docs/  (contenu à déposer dans src/content/docs/ d'Astro)
Découpage par catégorie : index, guides, api/, admin/, ops/.
"""
import os
import re
import unicodedata

ROOT = os.path.dirname(os.path.abspath(__file__))
SRC = os.path.join(ROOT, "docs")
OUT = os.path.join(ROOT, "astro-docs")

FENCE = re.compile(r"^(```|~~~)")


def slugify(text):
    text = text.replace("`", "")
    text = unicodedata.normalize("NFKD", text)
    text = "".join(c for c in text if not unicodedata.combining(c))
    text = text.lower()
    text = re.sub(r"[^a-z0-9]+", "-", text)
    return text.strip("-") or "page"


def yaml_q(s):
    return '"' + s.replace("\\", "\\\\").replace('"', '\\"') + '"'


def iter_with_fence(lines):
    """Yield (line, in_fence) ; bascule l'état AVANT de yield la ligne d'ouverture
    pour qu'une ligne de clôture compte comme dans le bloc."""
    in_fence = False
    for ln in lines:
        if FENCE.match(ln):
            yield ln, in_fence  # la ligne de fence elle-même n'est pas un titre
            in_fence = not in_fence
        else:
            yield ln, in_fence


def split_by_level(lines, level):
    """Coupe par titres de niveau exact `level`, en ignorant ce qui est dans
    un bloc de code. Retourne (preamble, [(title, body_lines)])."""
    hashes = "#" * level + " "
    preamble = []
    sections = []
    cur_title = None
    cur_body = []
    started = False
    for ln, in_fence in iter_with_fence(lines):
        is_head = (not in_fence) and ln.startswith(hashes) and not ln.startswith(hashes + "#")
        if is_head:
            if not started:
                started = True
            else:
                sections.append((cur_title, cur_body))
            cur_title = ln[len(hashes):].strip()
            cur_body = []
        else:
            if started:
                cur_body.append(ln)
            else:
                preamble.append(ln)
    if started:
        sections.append((cur_title, cur_body))
    return preamble, sections


def promote(lines, by):
    """Remonte les titres de `by` niveaux (### -> ## si by=1), fence-aware."""
    if by <= 0:
        return lines
    out = []
    for ln, in_fence in iter_with_fence(lines):
        m = re.match(r"^(#{1,6}) ", ln)
        if m and not in_fence:
            n = len(m.group(1))
            new = max(1, n - by)
            ln = "#" * new + ln[n:]
        out.append(ln)
    return out


def first_para(lines):
    buf = []
    for ln in lines:
        s = ln.strip()
        if not s:
            if buf:
                break
            continue
        if s.startswith(("#", "```", "|", "-", "*", ">")) or s.startswith("HTTP/"):
            if buf:
                break
            continue
        buf.append(s)
        if len(" ".join(buf)) > 160:
            break
    desc = " ".join(buf)
    desc = re.sub(r"`([^`]*)`", r"\1", desc)
    desc = re.sub(r"\[([^\]]*)\]\([^)]*\)", r"\1", desc)
    desc = re.sub(r"\*\*([^*]*)\*\*", r"\1", desc)
    desc = re.sub(r"\s+", " ", desc).strip()
    if len(desc) > 158:
        desc = desc[:155].rstrip() + "…"
    return desc


def write_page(relpath, title, description, body_lines):
    path = os.path.join(OUT, relpath)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    body = "\n".join(body_lines).strip("\n")
    fm = ["---", "title: " + yaml_q(title)]
    if description:
        fm.append("description: " + yaml_q(description))
    fm.append("---")
    with open(path, "w", encoding="utf-8") as f:
        f.write("\n".join(fm) + "\n\n" + body + "\n")
    return relpath


def read(name):
    with open(os.path.join(SRC, name), encoding="utf-8") as f:
        return f.read().splitlines()


generated = []

# ---------------------------------------------------------------- index.mdx
index = """---
title: Documentation Hydrogen
description: Framework PHP — API JSON publique, back-office d'administration, déploiement et outillage.
template: splash
hero:
  tagline: API JSON:API 1.1, back-office service-to-service, i18n et opérations.
  actions:
    - text: API publique
      link: /guides/conventions/
      icon: right-arrow
      variant: primary
    - text: API d'administration
      link: /admin/authentification/
      icon: external
---

import { Card, CardGrid } from '@astrojs/starlight/components';

<CardGrid stagger>
  <Card title="API publique" icon="open-book">
    Endpoints `/api/*` au format JSON:API 1.1 — auth, pagination, dates,
    erreurs, et toutes les ressources métier (médias, utilisateurs, géo…).
  </Card>
  <Card title="Administration" icon="setting">
    Surface `/admin/*` service-to-service (JSON plat + détails « 360° »),
    modération, utilisateurs, back-office staff.
  </Card>
  <Card title="Guides" icon="information">
    Conventions transverses : internationalisation et format d'erreur.
  </Card>
  <Card title="Opérations" icon="rocket">
    Déploiement FTP/FTPS et inventaire des scripts `bin/`.
  </Card>
</CardGrid>
"""
os.makedirs(OUT, exist_ok=True)
with open(os.path.join(OUT, "index.mdx"), "w", encoding="utf-8") as f:
    f.write(index)
generated.append("index.mdx")

# ---------------------------------------------------------------- api.md
api_lines = read("api.md")
_, api_secs = split_by_level(api_lines, 2)

GUIDE_TITLES = {
    "Conventions générales": "conventions",
    "Internationalisation": "internationalisation",
    "Format d'erreur JSON:API": "format-erreur",
}
SKIP = {"Table des matières"}

for title, body in api_secs:
    if title in SKIP:
        continue
    body_p = promote(body, 1)
    desc = first_para(body)
    if title in GUIDE_TITLES:
        rel = "guides/" + GUIDE_TITLES[title] + ".md"
    else:
        rel = "api/" + slugify(title) + ".md"
    generated.append(write_page(rel, title, desc, body_p))

# ---------------------------------------------------------------- admin.md
admin_lines = read("admin.md")
_, admin_secs = split_by_level(admin_lines, 2)

ADMIN_GROUPS = {
    "media": "Médias",
    "jobs": "Jobs & files",
    "cache": "Cache",
    "observabilite": "Observabilité & maintenance",
    "reports": "Signalements",
    "comments": "Commentaires",
    "users": "Utilisateurs",
    "tracking": "Tracking",
    "coupons": "Coupons",
    "newsletter": "Newsletter",
    "notifications": "Notifications",
    "countries": "Pays",
    "social-feeds": "Social feeds",
    "general": "Conventions des endpoints",
}
URL_RE = re.compile(r"/admin/([a-z\-]+)")
OBS = {"stats", "search", "health", "audit", "maintenance"}


def admin_group(h3title):
    m = URL_RE.search(h3title)
    if m:
        seg = m.group(1)
        if seg in OBS:
            return "observabilite"
        if seg in ADMIN_GROUPS:
            return seg
        return "general"
    low = h3title.lower()
    if "comment" in low:
        return "comments"
    if "user" in low:
        return "users"
    if "media" in low:
        return "media"
    return "general"


buckets = {k: [] for k in ADMIN_GROUPS}

for title, body in admin_secs:
    if title == "Endpoints":
        _, h3s = split_by_level(body, 3)
        for h3t, h3b in h3s:
            g = admin_group(h3t)
            block = ["### " + h3t] + h3b
            buckets[g].append(block)
    elif title == "Authentification":
        generated.append(write_page("admin/authentification.md", "Authentification",
                                    first_para(body), promote(body, 1)))
    elif title.startswith("Gestion de l'index pays"):
        block = ["### " + title] + body
        buckets["countries"].append(block)
    elif title.startswith("Back-office staff"):
        block = ["### " + title] + body
        buckets["users"].append(block)  # staff = gestion comptes back-office
    else:
        # Erreurs, Journal d'audit, Sécurité opérationnelle, Endpoints à venir
        generated.append(write_page("admin/" + slugify(title) + ".md", title,
                                    first_para(body), promote(body, 1)))

for g, blocks in buckets.items():
    if not blocks:
        continue
    flat = []
    for b in blocks:
        flat += b + [""]
    desc = first_para(promote(flat[1:], 1))
    generated.append(write_page("admin/" + g + ".md", ADMIN_GROUPS[g], desc, promote(flat, 1)))

# ---------------------------------------------------------------- ops (deploy + bin)
for name, rel, title in [("deploy.md", "ops/deploiement.md", "Déploiement FTP"),
                         ("bin.md", "ops/scripts-bin.md", "Scripts bin/")]:
    lines = read(name)
    pre, _secs = split_by_level(lines, 1)  # retire le H1, garde le reste
    # tout après le H1 :
    body = []
    started = False
    for ln, in_fence in iter_with_fence(lines):
        if not started:
            if ln.startswith("# ") and not in_fence:
                started = True
            continue
        body.append(ln)
    desc = first_para(body)
    generated.append(write_page(rel, title, desc, body))

print("FILES:", len(generated))
for g in sorted(generated):
    print(" ", g)
