"""
Génération du Plan d'affaire PDF conforme au modèle officiel (BP_complet.md).
En-tête et pied de page sur chaque page, mise en forme soignée.
"""
import io
from decimal import Decimal
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak, Frame, PageTemplate
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY

# Couleur thème : fond blanc, texte lisible, bordure type modèle officiel
THEME_GREEN = colors.HexColor("#0d5c3d")       # vert foncé (bordure externe)
THEME_GOLD = colors.HexColor("#b8860b")         # doré / marron (bordure interne cadre)
THEME_LIGHT = colors.HexColor("#047857")       # vert pour titres de section
BP_BG_PAGE = colors.white
BP_BG_CARD = colors.HexColor("#f8fafc")         # gris très clair pour tableaux
BP_TEXT_MAIN = colors.HexColor("#1e293b")      # texte principal
BP_TEXT_MUTED = colors.HexColor("#64748b")     # texte secondaire

def _txt(s):
    return (s or "").strip() or "—"

def _num(x):
    if x is None: return "0"
    try: return f"{float(x):,.0f}"
    except (TypeError, ValueError): return "0"

def _header_footer(canvas, doc, company_name):
    """Fond blanc + bordure décorative page 1 + pied de page."""
    canvas.saveState()
    canvas.setFillColor(BP_BG_PAGE)
    canvas.rect(0, 0, A4[0], A4[1], fill=1, stroke=0)
    # Bordure type modèle officiel (page de garde uniquement)
    if doc.page == 1:
        margin = 1.2 * cm
        canvas.setStrokeColor(THEME_GREEN)
        canvas.setLineWidth(1.5)
        canvas.rect(margin, margin, A4[0] - 2*margin, A4[1] - 2*margin, fill=0, stroke=1)
        canvas.setStrokeColor(THEME_GOLD)
        canvas.setLineWidth(0.8)
        canvas.rect(margin + 0.4*cm, margin + 0.4*cm, A4[0] - 2*margin - 0.8*cm, A4[1] - 2*margin - 0.8*cm, fill=0, stroke=1)
    canvas.setFont("Helvetica", 8)
    canvas.setFillColor(BP_TEXT_MUTED)
    canvas.drawString(2*cm, 1.2*cm, f"Plan d'affaire — {company_name[:50]}")
    canvas.drawRightString(A4[0] - 2*cm, 1.2*cm, f"Page {doc.page}")
    canvas.restoreState()

def generate_business_plan_pdf(bp, financial_data):
    buffer = io.BytesIO()
    company_name = _txt(getattr(bp, "company_name", None)) or "Projet"
    doc = SimpleDocTemplate(
        buffer,
        pagesize=A4,
        leftMargin=2*cm,
        rightMargin=2*cm,
        topMargin=2*cm,
        bottomMargin=2*cm,
    )
    elements = []
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(
        name='CenterTitle',
        parent=styles['Heading1'],
        alignment=TA_CENTER,
        spaceAfter=12,
        textColor=BP_TEXT_MAIN,
        fontSize=16,
    ))
    styles.add(ParagraphStyle(
        name='JustifyText',
        parent=styles['Normal'],
        alignment=TA_JUSTIFY,
        spaceAfter=8,
        fontSize=10,
        textColor=BP_TEXT_MAIN,
    ))
    styles.add(ParagraphStyle(
        name='CellText',
        parent=styles['Normal'],
        fontSize=9,
        spaceAfter=2,
        textColor=BP_TEXT_MAIN,
    ))

    def _cell(s, max_len=600):
        """Contenu de cellule : Paragraph pour texte long (retour à la ligne), sinon chaîne."""
        s = (s or "—").strip()
        if not s:
            return "—"
        if len(s) <= 70:
            return s[:100]
        return Paragraph(s[:max_len].replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"), styles['CellText'])
    styles.add(ParagraphStyle(name='BPBodyText', parent=styles['Normal'], textColor=BP_TEXT_MAIN, fontSize=10))
    styles.add(ParagraphStyle(
        name='Section',
        parent=styles['Heading2'],
        spaceBefore=16,
        spaceAfter=10,
        fontSize=13,
        textColor=THEME_GREEN,
    ))
    styles.add(ParagraphStyle(
        name='Subsection',
        parent=styles['Heading3'],
        spaceBefore=10,
        spaceAfter=6,
        fontSize=11,
        textColor=BP_TEXT_MAIN,
    ))

    costs = financial_data.get("global_costs") or {}
    pnl = financial_data.get("pnl_normal") or []
    loan_schedule = financial_data.get("loan_schedule") or []
    amort = financial_data.get("amortization") or {}
    amort_lines = amort.get("lines") or []
    pnl_rev = financial_data.get("pnl_sensitivity_revenue") or []
    pnl_cost = financial_data.get("pnl_sensitivity_costs") or []

    promoter_name = _txt(getattr(bp, "promoter_name", None)) or f"{getattr(bp.entrepreneur, 'last_name', '')} {getattr(bp.entrepreneur, 'first_name', '')}".strip() or "—"
    project_title = _txt(getattr(bp, "project_title", None)) or bp.company_name
    elaboration = ""
    if getattr(bp, "elaboration_month", None) and getattr(bp, "elaboration_year", None):
        elaboration = f"{bp.elaboration_month:02d} / {bp.elaboration_year}"
    else:
        elaboration = f"{bp.created_at.month:02d} / {bp.created_at.year}"

    # ——— PAGE DE GARDE (modèle officiel : Sénégal, cadre, porteur, date) ———
    styles.add(ParagraphStyle(
        name='CoverRepublic',
        alignment=TA_CENTER,
        fontSize=12,
        textColor=BP_TEXT_MAIN,
        spaceAfter=4,
    ))
    styles.add(ParagraphStyle(
        name='CoverMotto',
        alignment=TA_CENTER,
        fontSize=10,
        textColor=BP_TEXT_MAIN,
        spaceAfter=28,
    ))
    styles.add(ParagraphStyle(
        name='CoverModel',
        alignment=TA_CENTER,
        fontSize=12,
        textColor=BP_TEXT_MAIN,
        spaceAfter=8,
    ))
    styles.add(ParagraphStyle(
        name='CoverTitle',
        alignment=TA_CENTER,
        fontSize=14,
        textColor=THEME_GREEN,
        spaceAfter=24,
    ))
    styles.add(ParagraphStyle(
        name='CoverHolder',
        alignment=TA_CENTER,
        fontSize=10,
        textColor=BP_TEXT_MAIN,
        spaceAfter=6,
    ))
    styles.add(ParagraphStyle(
        name='CoverFooter',
        alignment=TA_CENTER,
        fontSize=9,
        textColor=BP_TEXT_MUTED,
        spaceAfter=0,
    ))
    elements.append(Spacer(1, 140))
    elements.append(Paragraph("<b>REPUBLIQUE DU SENEGAL</b>", styles['CoverRepublic']))
    elements.append(Paragraph("Un Peuple - Un But - Une Foi", styles['CoverMotto']))
    # Cadre central (bordure dorée, fond blanc)
    title_quoted = f'"{project_title[:70]}"' if project_title else '"—"'
    data_cover_box = [
        [Paragraph("MODÈLE DE PLAN D'AFFAIRE", styles['CoverModel'])],
        [Paragraph(title_quoted, styles['CoverTitle'])],
    ]
    t_cover_box = Table(data_cover_box, colWidths=[12*cm])
    t_cover_box.setStyle(TableStyle([
        ('BOX', (0, 0), (-1, -1), 1.2, THEME_GOLD),
        ('BACKGROUND', (0, 0), (-1, -1), colors.white),
        ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('PADDING', (0, 0), (-1, -1), 16),
    ]))
    elements.append(t_cover_box)
    elements.append(Spacer(1, 50))
    elements.append(Paragraph("Porteur du projet :", styles['CoverHolder']))
    elements.append(Paragraph(f"<b>{company_name}</b>", styles['CoverHolder']))
    elements.append(Spacer(1, 8))
    elements.append(Paragraph("Mois et année d'élaboration :", styles['CoverHolder']))
    elements.append(Paragraph(f"<b>{elaboration}</b>", styles['CoverHolder']))
    elements.append(Spacer(1, 60))
    elements.append(Paragraph(f"Document généré — {company_name}", styles['CoverFooter']))
    elements.append(PageBreak())

    # ——— I. PRÉSENTATION DU PROMOTEUR ———
    elements.append(Paragraph("I. PRÉSENTATION DU PROMOTEUR", styles['Section']))

    elements.append(Paragraph("1. Le Promoteur", styles['Subsection']))
    creation_date_str = bp.creation_date.strftime("%d/%m/%Y") if getattr(bp, "creation_date", None) else "—"
    data_prom = [
        ["Nom", _cell(promoter_name)],
        ["Raison sociale", _cell(bp.company_name)],
        ["Statut juridique", _cell(bp.legal_status)],
        ["Date de création", creation_date_str],
        ["Numéro récépissé (association)", _cell(bp.receipt_number)],
        ["Numéro du registre de commerce", _cell(bp.rc_number)],
        ["Numéro du NINEA", _cell(bp.ninea)],
        ["Adresse", _cell(bp.address)],
    ]
    t_prom = Table(data_prom, colWidths=[130, 270])
    t_prom.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MAIN),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_prom)
    elements.append(Spacer(1, 10))

    elements.append(Paragraph("Personne à contacter", styles['Subsection']))
    data_contact = [
        ["Prénom et Nom", _cell(bp.contact_name)],
        ["Téléphone", _txt(bp.contact_phone) or _txt(getattr(bp.entrepreneur, "phone", None))],
        ["E-mail", _txt(bp.contact_email) or _txt(bp.entrepreneur.email)],
        ["Adresse", _cell(bp.contact_address)],
    ]
    t_contact = Table(data_contact, colWidths=[130, 270])
    t_contact.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MAIN),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_contact)
    elements.append(Spacer(1, 14))

    elements.append(Paragraph("2. Capacité et compétences du ou des promoteurs par rapport au projet", styles['Subsection']))
    capacity = _txt(getattr(bp, "capacity_reasons", None))
    if capacity:
        elements.append(Paragraph("<b>Raisons des capacités à mener le projet :</b>", styles['BPBodyText']))
        elements.append(Paragraph(capacity, styles['JustifyText']))
    elements.append(Paragraph("<b>Votre expérience :</b>", styles['BPBodyText']))
    elements.append(Paragraph(_txt(bp.experience_text), styles['JustifyText']))
    elements.append(Paragraph("<b>Votre formation :</b>", styles['BPBodyText']))
    elements.append(Paragraph(_txt(bp.training_text), styles['JustifyText']))
    elements.append(Paragraph("<b>Vos compétences :</b>", styles['BPBodyText']))
    elements.append(Paragraph(_txt(bp.skills_text), styles['JustifyText']))
    elements.append(PageBreak())

    # ——— II. PRÉSENTATION DU PROJET ———
    elements.append(Paragraph("II. PRÉSENTATION DU PROJET", styles['Section']))
    elements.append(Paragraph("2.1 Objet du projet", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.project_object), styles['JustifyText']))
    elements.append(Paragraph("2.2 Objectifs du projet", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.project_objectives), styles['JustifyText']))
    elements.append(Paragraph("2.3 Localisation du projet", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.location_reason), styles['JustifyText']))
    elements.append(PageBreak())

    # ——— III. ÉTUDE DE MARCHÉ ———
    elements.append(Paragraph("III. ÉTUDE DE MARCHÉ", styles['Section']))
    elements.append(Paragraph("3.1 Présentation du produit", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.product_description) or "—", styles['JustifyText']))
    elements.append(Paragraph("3.2 Étude de la concurrence", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.competition_analysis) or "—", styles['JustifyText']))
    elements.append(Paragraph("3.3 Étude de la clientèle", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.customer_analysis) or "—", styles['JustifyText']))
    elements.append(Paragraph("3.4 La stratégie marketing", styles['Subsection']))
    marketing_activities = getattr(bp, "marketing_activities", None)
    if marketing_activities is not None and hasattr(marketing_activities, "all"):
        acts = list(marketing_activities.all())
        if acts:
            data_act = [["ACTIVITÉS", "DESCRIPTION"]]
            for a in acts:
                data_act.append([_cell(getattr(a, "activity_name", "")), _cell(getattr(a, "description", ""))])
            t_act = Table(data_act, colWidths=[180, 280])
            t_act.setStyle(TableStyle([
                ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
                ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
                ('BACKGROUND', (0, 1), (-1, -1), BP_BG_CARD),
                ('TEXTCOLOR', (0, 1), (-1, -1), BP_TEXT_MUTED),
                ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
                ('PADDING', (0, 0), (-1, -1), 8),
            ]))
            elements.append(t_act)
    if _txt(bp.marketing_strategy):
        elements.append(Paragraph(_txt(bp.marketing_strategy), styles['JustifyText']))
    elements.append(PageBreak())

    # ——— 3. ÉTUDE TECHNIQUE ———
    elements.append(Paragraph("3. ÉTUDE TECHNIQUE", styles['Section']))
    elements.append(Paragraph("4.1 Le processus de production", styles['Subsection']))
    elements.append(Paragraph(_txt(bp.process_description) or "—", styles['JustifyText']))

    investments = list(bp.investments.all())
    incorp = [i for i in investments if i.invest_type == "INCORPOREL"]
    corp = [i for i in investments if i.invest_type == "CORPOREL"]

    elements.append(Paragraph("Immobilisations incorporelles", styles['Subsection']))
    data_incorp = [["DÉSIGNATION", "DESCRIPTION"]]
    for i in incorp:
        data_incorp.append([_cell(i.name), _cell(getattr(i, "description", ""))])
    if len(data_incorp) > 1:
        t_incorp = Table(data_incorp, colWidths=[200, 260])
        t_incorp.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 8),
        ]))
        elements.append(t_incorp)
    else:
        elements.append(Paragraph("—", styles['BPBodyText']))

    elements.append(Paragraph("Immobilisations corporelles", styles['Subsection']))
    data_corp = [["DÉSIGNATION", "QUANTITÉ", "DESCRIPTION"]]
    for i in corp:
        data_corp.append([_txt(i.name), str(i.quantity), _txt(getattr(i, "description", ""))])
    if len(data_corp) > 1:
        t_corp = Table(data_corp, colWidths=[160, 70, 230])
        t_corp.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 8),
        ]))
        elements.append(t_corp)
    else:
        elements.append(Paragraph("—", styles['BPBodyText']))

    elements.append(Paragraph("Le Besoin en Ressources Humaines", styles['Subsection']))
    hr_list = list(bp.human_resources.all())
    data_hr = [["DÉSIGNATION DU POSTE", "NOMBRE", "DESCRIPTION DU POSTE"]]
    for h in hr_list:
        data_hr.append([_cell(h.position), str(h.count), _cell(h.description)])
    if len(data_hr) > 1:
        t_hr = Table(data_hr, colWidths=[160, 55, 245])
        t_hr.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 8),
        ]))
        elements.append(t_hr)
    else:
        elements.append(Paragraph("—", styles['BPBodyText']))
    elements.append(PageBreak())

    # ——— 4. ÉTUDE FINANCIÈRE ———
    elements.append(Paragraph("4. ETUDE FINANCIÈRE", styles['Section']))
    elements.append(Paragraph("La détermination du coût du projet", styles['Subsection']))

    elements.append(Paragraph("Coût des immobilisations incorporelles", styles['BPBodyText']))
    data_ci = [["DÉSIGNATION", "COÛT"]]
    tot_incorp = 0
    for i in incorp:
        tot_incorp += float(i.total_cost)
        data_ci.append([_cell(i.name), _num(i.total_cost)])
    data_ci.append(["TOTAL", _num(tot_incorp)])
    t_ci = Table(data_ci, colWidths=[280, 180])
    t_ci.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
        ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_ci)
    elements.append(Spacer(1, 8))

    elements.append(Paragraph("Coût des immobilisations corporelles", styles['BPBodyText']))
    data_cc = [["DÉSIGNATION", "QUANTITÉ", "PRIX UNITAIRE", "MONTANT"]]
    tot_corp = 0
    for i in corp:
        tot_corp += float(i.total_cost)
        data_cc.append([_cell(i.name), str(i.quantity), _num(i.unit_price), _num(i.total_cost)])
    data_cc.append(["TOTAL", "", "", _num(tot_corp)])
    t_cc = Table(data_cc, colWidths=[150, 65, 85, 120])
    t_cc.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
        ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_cc)
    elements.append(Spacer(1, 8))

    elements.append(Paragraph("Le Besoin en Fonds de Roulement (BFR)", styles['BPBodyText']))
    startup = list(bp.startup_costs.all())
    for cat_label, cat_code in [("Besoins en main d'œuvre", "LABOR"), ("Besoins en matières premières", "MATERIAL"), ("Charges fixes", "FIXED")]:
        rows = [r for r in startup if r.category == cat_code]
        if not rows:
            continue
        data_bfr = [["DÉSIGNATION", "MONTANT MENSUEL", "NOMBRE DE MOIS", "TOTAL"]]
        sub_tot = 0
        for r in rows:
            tot_bfr = float(r.monthly_amount) * int(r.months_needed)
            sub_tot += tot_bfr
            data_bfr.append([_cell(r.designation), _num(r.monthly_amount), str(r.months_needed), _num(tot_bfr)])
        data_bfr.append(["TOTAL", "", "", _num(sub_tot)])
        elements.append(Paragraph(cat_label, styles['BPBodyText']))
        t_bfr = Table(data_bfr, colWidths=[160, 90, 90, 110])
        t_bfr.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
            ('PADDING', (0, 0), (-1, -1), 8),
        ]))
        elements.append(t_bfr)
        elements.append(Spacer(1, 6))

    tot_bfr = costs.get("bfr", 0) or sum(float(c.monthly_amount) * c.months_needed for c in startup)
    total_project = float(costs.get("total_project", 0) or (tot_incorp + tot_corp + tot_bfr))
    elements.append(Paragraph("Récapitulatif des besoins", styles['BPBodyText']))
    data_recap = [
        ["Immobilisations incorporelles", _num(costs.get("incorporel", tot_incorp))],
        ["Immobilisations corporelles", _num(costs.get("corporel", tot_corp))],
        ["Besoin en Fonds de Roulement", _num(costs.get("bfr", tot_bfr))],
        ["COÛT TOTAL DU PROJET", _num(total_project)],
    ]
    t_recap = Table(data_recap, colWidths=[280, 180])
    t_recap.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
        ('BACKGROUND', (0, -1), (-1, -1), THEME_GREEN),
        ('TEXTCOLOR', (0, -1), (-1, -1), colors.white),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_recap)
    elements.append(Spacer(1, 14))

    elements.append(Paragraph("2. Le Plan de financement", styles['Subsection']))
    loan_amount = float(costs.get("loan_amount", 0))
    periodicity = getattr(bp, "get_loan_repayment_periodicity_display", lambda: str(bp.loan_repayment_periodicity))()
    data_fin = [
        ["Coût du projet", _num(total_project)],
        ["Apport personnel", _num(bp.personal_contribution)],
        ["Autres (spécifier)", _num(getattr(bp, "other_funding", 0))],
        ["Crédit sollicité", _num(loan_amount)],
        ["Taux d'intérêt", f"{float(bp.loan_interest_rate):.2f} %"],
        ["Différé (en mois)", str(getattr(bp, "loan_deferral_months", 0) or 0)],
        ["Durée du remboursement", f"{bp.loan_duration_months} mois"],
        ["Périodicité de remboursement", periodicity],
    ]
    t_fin = Table(data_fin, colWidths=[250, 210])
    t_fin.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('PADDING', (0, 0), (-1, -1), 8),
    ]))
    elements.append(t_fin)
    elements.append(Spacer(1, 12))

    elements.append(Paragraph("3. Tableau de remboursement", styles['Subsection']))
    data_loan = [["Période", "Capital début période", "Intérêts", "Amortissement du capital", "Annuité"]]
    for row in loan_schedule:
        data_loan.append([
            row.get("year", ""),
            _num(row.get("capital_start")),
            _num(row.get("interest")),
            _num(row.get("amortization")),
            _num(row.get("annuity")),
        ])
    if len(data_loan) > 1:
        t_loan = Table(data_loan, colWidths=[60, 85, 65, 95, 75])
        t_loan.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(t_loan)
    else:
        elements.append(Paragraph("—", styles['BPBodyText']))
    elements.append(Spacer(1, 12))

    elements.append(Paragraph("4. Tableau d'amortissement des immobilisations", styles['Subsection']))
    data_amort = [["IMMOBILISATION", "VALEUR À L'ACHAT", "DURÉE PRÉVUE (années)", "AMORTISSEMENT ANNUEL"]]
    for line in amort_lines:
        data_amort.append([
            _cell(line.get("name")),
            _num(line.get("value")),
            str(line.get("duration", "")),
            _num(line.get("annuity")),
        ])
    total_ann = amort.get("total_annual_amortization", 0)
    if len(data_amort) > 1:
        data_amort.append(["TOTAL AMORTISSEMENT", "", "", _num(total_ann)])
        t_amort = Table(data_amort, colWidths=[160, 95, 95, 110])
        t_amort.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
            ('PADDING', (0, 0), (-1, -1), 8),
        ]))
        elements.append(t_amort)
    else:
        elements.append(Paragraph("—", styles['BPBodyText']))
    elements.append(Spacer(1, 12))

    elements.append(Paragraph("5. Les Données d'exploitation – Charges d'exploitation (Année 1 à 4)", styles['Subsection']))
    expenses_list = list(bp.expenses.all())
    if expenses_list:
        data_exp = [["DÉSIGNATION", "Année 1", "Année 2", "Année 3", "Année 4"]]
        for e in expenses_list:
            data_exp.append([_txt(e.designation), _num(e.year_1), _num(e.year_2), _num(e.year_3), _num(e.year_4)])
        totals_y = [0, 0, 0, 0]
        for e in expenses_list:
            totals_y[0] += float(e.year_1 or 0)
            totals_y[1] += float(e.year_2 or 0)
            totals_y[2] += float(e.year_3 or 0)
            totals_y[3] += float(e.year_4 or 0)
        data_exp.append(["TOTAL", _num(totals_y[0]), _num(totals_y[1]), _num(totals_y[2]), _num(totals_y[3])])
        t_exp = Table(data_exp, colWidths=[180, 75, 75, 75, 75])
        t_exp.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
            ('PADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(t_exp)
    elements.append(Spacer(1, 8))

    elements.append(Paragraph("Détermination des recettes d'exploitation (Année 1 à 4)", styles['Subsection']))
    revenues_list = list(bp.revenues.all())
    if revenues_list:
        data_rev = [["DÉSIGNATION", "Année 1", "Année 2", "Année 3", "Année 4"]]
        for r in revenues_list:
            data_rev.append([_cell(r.product_name), _num(r.year_1), _num(r.year_2), _num(r.year_3), _num(r.year_4)])
        totals_y = [0, 0, 0, 0]
        for r in revenues_list:
            totals_y[0] += float(r.year_1 or 0)
            totals_y[1] += float(r.year_2 or 0)
            totals_y[2] += float(r.year_3 or 0)
            totals_y[3] += float(r.year_4 or 0)
        data_rev.append(["TOTAL", _num(totals_y[0]), _num(totals_y[1]), _num(totals_y[2]), _num(totals_y[3])])
        t_rev = Table(data_rev, colWidths=[180, 75, 75, 75, 75])
        t_rev.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
            ('PADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(t_rev)
    elements.append(PageBreak())

    elements.append(Paragraph("6. Le Compte d'Exploitation Prévisionnel", styles['Subsection']))
    rows_pnl = [
        ("A. RECETTES", "revenues"),
        ("B. DÉPENSES", "expenses"),
        ("C. Résultat Brut d'Exploitation (A-B)", "rbe"),
        ("D. Amortissement", "amortization"),
        ("E. Intérêt", "interest"),
        ("F. Résultat avant impôt (C-D-E)", "rai"),
        ("G. Impôt", "impot"),
        ("H. Résultat Net (F-G)", "net_result"),
        ("I. Cash-flows (H+D)", "cash_flow"),
    ]
    headers_pnl = ["DÉSIGNATION"] + [f"Année {row['year']}" for row in pnl]
    data_pnl = [headers_pnl]
    for label, key in rows_pnl:
        row = [label]
        for y_data in pnl:
            row.append(_num(y_data.get(key)))
        data_pnl.append(row)
    t_pnl = Table(data_pnl, colWidths=[200, 70, 70, 70, 70])
    t_pnl.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
        ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
        ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('BACKGROUND', (0, 8), (-1, 8), THEME_LIGHT),
        ('TEXTCOLOR', (0, 8), (-1, 8), colors.black),
        ('FONTNAME', (0, 8), (-1, 8), 'Helvetica-Bold'),
        ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
        ('PADDING', (0, 0), (-1, -1), 6),
    ]))
    elements.append(t_pnl)
    elements.append(Spacer(1, 14))

    elements.append(Paragraph("5.8. Test de Sensibilité", styles['Subsection']))
    elements.append(Paragraph("Première hypothèse : les recettes baissent de 10 %", styles['BPBodyText']))
    if pnl_rev:
        data_sr = [["DÉSIGNATION"] + [f"Année {row['year']}" for row in pnl_rev]]
        for label, key in rows_pnl:
            data_sr.append([label] + [_num(y.get(key)) for y in pnl_rev])
        t_sr = Table(data_sr, colWidths=[200, 70, 70, 70, 70])
        t_sr.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(t_sr)
    elements.append(Spacer(1, 10))
    elements.append(Paragraph("Deuxième hypothèse : les charges augmentent de 10 %", styles['BPBodyText']))
    if pnl_cost:
        data_sc = [["DÉSIGNATION"] + [f"Année {row['year']}" for row in pnl_cost]]
        for label, key in rows_pnl:
            data_sc.append([label] + [_num(y.get(key)) for y in pnl_cost])
        t_sc = Table(data_sc, colWidths=[200, 70, 70, 70, 70])
        t_sc.setStyle(TableStyle([
            ('BACKGROUND', (0, 0), (-1, -1), BP_BG_CARD),
            ('TEXTCOLOR', (0, 0), (-1, -1), BP_TEXT_MUTED),
            ('BACKGROUND', (0, 0), (-1, 0), THEME_GREEN),
            ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
            ('GRID', (0, 0), (-1, -1), 0.5, THEME_GREEN),
            ('PADDING', (0, 0), (-1, -1), 6),
        ]))
        elements.append(t_sc)

    doc.build(
        elements,
        onFirstPage=lambda c, d: _header_footer(c, d, company_name),
        onLaterPages=lambda c, d: _header_footer(c, d, company_name),
    )
    buffer.seek(0)
    return buffer
