#!/usr/bin/env python3
"""
Rend le digest du matin (DIGEST-DUJOUR-[date].md) dans le template HTML
Fidulia validé le 14/07/2026, puis l'envoie vers le webhook n8n
(workflow VEILLE-DIGEST-EMAIL).

Remplace send_digest_webhook.py (ancien CSS basique) comme étape finale de
ROUTINE-VEILLE-MATIN.md §5.4.

Usage:
    python3 render_digest.py /chemin/vers/DIGEST-DUJOUR-2026-07-15.md
    python3 render_digest.py /chemin/vers/DIGEST-DUJOUR-2026-07-15.md --no-send  (aperçu HTML sur stdout, pas d'envoi)
"""
import argparse
import html as html_module
import json
import os
import re
import sys
import urllib.request
import urllib.error

ENV_FILE = os.path.expanduser("~/.config/fidulia/veille.env")
TEMPLATE_PATH = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), "..", "templates", "digest-email.template.html"
)


def load_env_file(path):
    values = {}
    try:
        with open(path, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith("#"):
                    continue
                if line.startswith("export "):
                    line = line[len("export "):]
                if "=" not in line:
                    continue
                key, _, val = line.partition("=")
                values[key.strip()] = val.strip().strip('"').strip("'")
    except OSError:
        pass
    return values


def esc(text: str) -> str:
    return html_module.escape((text or "").strip(), quote=True)


def inline(text: str) -> str:
    """Échappe puis convertit les liens [texte](url) et **gras** markdown en HTML."""
    s = esc(text)
    s = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'<a href="\2">\1</a>', s)
    s = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', s)
    return s


def parse_sections(md_text: str) -> dict:
    """Découpe le markdown en sections par titre H2 ('## ...'). Retourne {header: body}."""
    sections = {}
    current_header = None
    current_body = []
    for line in md_text.split("\n"):
        if line.startswith("## "):
            if current_header is not None:
                sections[current_header] = "\n".join(current_body).strip()
            current_header = line[3:].strip()
            current_body = []
        elif current_header is not None:
            current_body.append(line)
    if current_header is not None:
        sections[current_header] = "\n".join(current_body).strip()
    return sections


def find_section(sections: dict, keyword: str):
    for header, body in sections.items():
        if keyword.lower() in header.lower():
            return header, body
    return None, None


def parse_md_table(body: str):
    """Retourne (colonnes_en_tête, [lignes]) pour la première table markdown trouvée dans body."""
    lines = [l for l in body.split("\n") if l.strip().startswith("|")]
    if len(lines) < 2:
        return [], []
    header_cells = [c.strip() for c in lines[0].split("|")[1:-1]]
    rows = []
    for line in lines[2:]:  # ligne 1 = header, ligne 2 = séparateur |---|
        cells = [c.strip() for c in line.split("|")[1:-1]]
        if cells:
            rows.append(cells)
    return header_cells, rows


def extract_int(pattern: str, text: str, default=0) -> int:
    m = re.search(pattern, text)
    return int(m.group(1)) if m else default


def render_prospection(body: str) -> str:
    if not body:
        return ""
    envoyees = extract_int(r'Invitations envoyées\s*:\*\*\s*(\d+)', body)
    total = extract_int(r'Invitations envoyées\s*:\*\*\s*\d+/(\d+)', body, default=10)
    ecartes = extract_int(r'Profils écartés dans ce batch\s*:\*\*\s*(\d+)', body)
    restants = extract_int(r'Profils restants dans la base\s*:\*\*\s*(\d+)', body)

    _, rows = parse_md_table(body)
    table_rows = ""
    for cells in rows:
        prenom, nom = (cells + ["", ""])[:2]
        cabinet = cells[2] if len(cells) > 2 else ""
        ville = cells[3] if len(cells) > 3 else ""
        table_rows += (
            f"<tr><td>{esc(prenom)} {esc(nom)}</td>"
            f"<td>{esc(cabinet)}</td><td>{esc(ville)}</td></tr>\n"
        )

    table_html = (
        f'<div class="sub-label">Profils invités aujourd\'hui</div>\n'
        f'<table class="dark-table"><thead><tr><th>Prénom Nom</th><th>Cabinet</th><th>Zone</th></tr></thead>'
        f'<tbody>{table_rows}</tbody></table>'
        if table_rows else ""
    )

    return f"""<div class="section dark">
    <div class="section-title">📊 Prospection</div>
    <div class="stats">
      <div class="stat-card"><div class="stat-number num-red">{envoyees}</div><div class="stat-label">invitations envoyées / {total}</div></div>
      <div class="stat-card"><div class="stat-number num-gray">{ecartes}</div><div class="stat-label">écartés ce batch</div></div>
      <div class="stat-card"><div class="stat-number num-gray">{restants}</div><div class="stat-label">profils restants</div></div>
    </div>
    {table_html}
  </div>"""


def render_acceptations(body: str) -> str:
    if not body:
        return ""
    if re.search(r'aucune nouvelle connexion', body, re.IGNORECASE):
        return """<div class="section">
    <div class="section-title">✅ Invitations acceptées</div>
    <div class="empty-note">Aucune nouvelle connexion aujourd'hui.</div>
  </div>"""

    _, rows = parse_md_table(body)
    table_rows = "".join(
        f"<tr><td>{esc(cells[0] if len(cells) > 0 else '')}</td>"
        f"<td>{esc(cells[1] if len(cells) > 1 else '')}</td></tr>\n"
        for cells in rows
    )
    total = extract_int(r'Total acceptations\s*:\s*(\d+)', body, default=len(rows))

    return f"""<div class="section">
    <div class="section-title">✅ Invitations acceptées ({total})</div>
    <table class="light-table"><thead><tr><th>Nom</th><th>Email récupéré</th></tr></thead>
    <tbody>{table_rows}</tbody></table>
  </div>"""


def render_engagement(body: str) -> str:
    if not body:
        return ""
    if re.search(r'rien à commenter', body, re.IGNORECASE):
        return """<div class="section">
    <div class="section-title">💬 À commenter aujourd'hui</div>
    <div class="empty-note">Rien à commenter aujourd'hui.</div>
  </div>"""

    blocks = re.split(r'\n-{3,}\n', body)
    cards = ""
    for block in blocks:
        m = re.match(
            r'###\s*(.+?)\n(.+?)\n\s*\n>\s*(.+?)\n\s*\n🔗\s*\[Voir le post\]\((.+?)\)',
            block.strip(),
            re.DOTALL,
        )
        if not m:
            continue
        auteur, sujet, commentaire, url = m.groups()
        cards += (
            '<div class="comment-card">'
            f'<div class="author">{esc(auteur)}</div>'
            f'<div class="subject">{esc(sujet)}</div>'
            f'<div class="quote">« {esc(commentaire)} »</div>'
            f'<a href="{esc(url)}">→ Voir le post</a>'
            '</div>\n'
        )

    if not cards:
        return ""

    return f"""<div class="section">
    <div class="section-title">💬 À commenter aujourd'hui</div>
    {cards}
  </div>"""


def render_veille(body: str) -> str:
    if not body:
        return ""
    if re.search(r'rien de notable', body, re.IGNORECASE):
        return """<div class="section">
    <div class="section-title">📰 Veille du jour</div>
    <div class="empty-note">Rien de notable aujourd'hui.</div>
  </div>"""

    signals = ""
    for line in body.split("\n"):
        line = line.strip()
        if line.startswith("- "):
            signals += (
                '<div class="signal"><div class="signal-dot"></div>'
                f'<div class="signal-text">{inline(line[2:])}</div></div>\n'
            )

    if not signals:
        return ""

    return f"""<div class="section">
    <div class="section-title">📰 Veille du jour</div>
    {signals}
  </div>"""


def extract_date_longue(md_text: str) -> str:
    for line in md_text.split("\n"):
        if line.startswith("# "):
            title = line[2:].strip()
            if "—" in title:
                return title.split("—", 1)[1].strip()
            return title
    return ""


def extract_subject(date_longue: str) -> str:
    return f"Digest Fidulia — {date_longue}" if date_longue else "Digest Fidulia"


def build_html(md_text: str) -> tuple:
    sections = parse_sections(md_text)
    _, prospection_body = find_section(sections, "Prospection")
    _, acceptations_body = find_section(sections, "acceptées")
    _, engagement_body = find_section(sections, "commenter")
    _, veille_body = find_section(sections, "Veille du jour")

    prospection_html = render_prospection(prospection_body)
    acceptations_html = render_acceptations(acceptations_body)
    engagement_html = render_engagement(engagement_body)
    veille_html = render_veille(veille_body)

    routines_present = sum([
        bool(prospection_html),
        bool(acceptations_html),
        bool(engagement_html or veille_html),
    ])
    if routines_present == 3:
        status_class, status_label = "ok", "✓ complètes"
    else:
        status_class, status_label = "warn", f"⚠ {routines_present}/3"

    date_longue = extract_date_longue(md_text)

    with open(TEMPLATE_PATH, "r", encoding="utf-8") as f:
        template = f.read()

    html_out = (
        template
        .replace("{{TITLE}}", esc(extract_subject(date_longue)))
        .replace("{{DATE_LONGUE}}", esc(date_longue))
        .replace("{{ROUTINES_COUNT}}", "3")
        .replace("{{ROUTINES_STATUS_CLASS}}", status_class)
        .replace("{{ROUTINES_STATUS_LABEL}}", status_label)
        .replace("{{PROSPECTION_SECTION}}", prospection_html)
        .replace("{{ACCEPTATIONS_SECTION}}", acceptations_html)
        .replace("{{ENGAGEMENT_SECTION}}", engagement_html)
        .replace("{{VEILLE_SECTION}}", veille_html)
    )
    return html_out, extract_subject(date_longue)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("digest_path", help="Chemin vers DIGEST-DUJOUR-[date].md")
    parser.add_argument("--endpoint", default="veille-digest-fidulia")
    parser.add_argument("--no-send", action="store_true", help="Affiche le HTML sur stdout sans envoyer")
    args = parser.parse_args()

    try:
        with open(args.digest_path, "r", encoding="utf-8") as f:
            md_text = f.read()
    except OSError as e:
        print(f"Impossible de lire {args.digest_path}: {e}", file=sys.stderr)
        sys.exit(1)

    html_out, subject = build_html(md_text)

    if args.no_send:
        print(html_out)
        return

    host = os.environ.get("N8N_HOST")
    token = os.environ.get("FIDULIA_TOKEN")
    if not host or not token:
        file_values = load_env_file(ENV_FILE)
        host = host or file_values.get("N8N_HOST")
        token = token or file_values.get("FIDULIA_TOKEN")
    if not host or not token:
        print("N8N_HOST et FIDULIA_TOKEN introuvables.", file=sys.stderr)
        sys.exit(1)

    payload = json.dumps({"subject": subject, "html": html_out}, ensure_ascii=False).encode("utf-8")
    req = urllib.request.Request(
        f"{host.rstrip('/')}/webhook/{args.endpoint}",
        data=payload,
        headers={"X-Fidulia-Token": token, "Content-Type": "application/json"},
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            print("Réponse n8n:", resp.read().decode("utf-8"))
    except urllib.error.URLError as e:
        print(f"Erreur POST {args.endpoint}: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()
