#!/usr/bin/env python3
"""
Doany.ai — Seed Financial Model (Refreshed April 10, 2026)
Updated pricing, hiring timeline, and growth assumptions.
Three scenarios: Conservative, Base, Optimistic
5-year projections (Year 1 = April 2026 – March 2027)
"""

import csv
import math
import os
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Tuple

# ═══════════════════════════════════════════════════════════════════
# CONFIGURATION
# ═══════════════════════════════════════════════════════════════════

STARTING_CUSTOMERS = 58
STARTING_MRR = 8_410
STARTING_CASH = 1_221_090
CURRENT_ARPU = STARTING_MRR / STARTING_CUSTOMERS  # ~$145

MIGRATION_START_MONTH = 2   # May 2026 = month 2
PRICING_CHURN_CUSTOMERS = round(58 * 0.60 * 0.15)  # ~5 Starter customers churn on price hike

SEED_AMOUNT = 5_000_000
SEED_MONTH = 3  # June 2026

BENEFITS_MULT = 1.35
COGS_PCT = 0.18
TOTAL_MONTHS = 60

# ── Current team monthly costs (fully loaded) ────────────────────

# R&D: 7 people — CTO 165K, Sr BE 155K, FS 145K, ML 160K, FE 140K, DevOps 150K, Designer 130K
_RD_BASE = [165, 155, 145, 160, 140, 150, 130]
CURRENT_RD_MONTHLY = sum(s * 1000 * BENEFITS_MULT / 12 for s in _RD_BASE)

# S&M: 5 people — HoS 135K+45K comm, AE 95K+45K, SDR 65K+20K, CS 90K, HoM 140K
_SM_BASE = [135, 95, 65, 90, 140]
_SM_COMM = [45, 45, 20, 0, 0]
CURRENT_SM_MONTHLY = sum(s * 1000 * BENEFITS_MULT / 12 for s in _SM_BASE) + sum(c * 1000 / 12 for c in _SM_COMM)

# G&A: 2 people — CEO 165K, Office Mgr 72K
_GA_BASE = [165, 72]
CURRENT_GA_MONTHLY = sum(s * 1000 * BENEFITS_MULT / 12 for s in _GA_BASE)

# Non-headcount costs
RD_TOOLING = 3_000
GA_OVERHEAD_START = 12_000
GA_OVERHEAD_Y3 = 25_000
SM_MKTG_START = 8_000
SM_MKTG_Y2_END = 35_000

# ── Role costs (annual fully loaded) ─────────────────────────────

ROLE_COST = {
    'engineer':      150_000 * BENEFITS_MULT,
    'eng_manager':   175_000 * BENEFITS_MULT,
    'pm':            140_000 * BENEFITS_MULT,
    'designer':      130_000 * BENEFITS_MULT,
    'sales_rep':     100_000 * BENEFITS_MULT + 40_000,
    'sales_manager': 150_000 * BENEFITS_MULT + 50_000,
    'cs':             90_000 * BENEFITS_MULT,
    'marketing':     130_000 * BENEFITS_MULT,
    'hr_ops':        110_000 * BENEFITS_MULT,
    'finance':       120_000 * BENEFITS_MULT,
}

# ── Hiring plan (month, role, department, count) ─────────────────
# Month 1 = April 2026

HIRES: List[Tuple[int, str, str, int]] = [
    # Q2 2026
    (2, 'engineer', 'R&D', 2),
    # Q3 2026
    (4, 'pm', 'R&D', 1),
    (5, 'designer', 'R&D', 1),
    # Q4 2026
    (7, 'engineer', 'R&D', 2),
    (8, 'sales_rep', 'S&M', 1),
    (9, 'cs', 'S&M', 1),
    # Q1 2027
    (10, 'eng_manager', 'R&D', 1),
    (11, 'sales_rep', 'S&M', 2),
    (12, 'marketing', 'S&M', 1),
    # Q2 2027
    (13, 'engineer', 'R&D', 2),
    (14, 'engineer', 'R&D', 1),
    (14, 'sales_manager', 'S&M', 1),
    (15, 'hr_ops', 'G&A', 1),
    # H2 2027
    (16, 'engineer', 'R&D', 1),
    (17, 'engineer', 'R&D', 1),
    (18, 'engineer', 'R&D', 1),
    (19, 'engineer', 'R&D', 1),
    (16, 'sales_rep', 'S&M', 1),
    (18, 'sales_rep', 'S&M', 1),
    (20, 'sales_rep', 'S&M', 1),
    (19, 'cs', 'S&M', 1),
    (21, 'finance', 'G&A', 1),
]

# Build lookup
HIRES_BY_MONTH: Dict[int, List] = {}
for m, role, dept, cnt in HIRES:
    HIRES_BY_MONTH.setdefault(m, []).append((role, dept, cnt))

# ═══════════════════════════════════════════════════════════════════
# SCENARIOS
# ═══════════════════════════════════════════════════════════════════

@dataclass
class Scenario:
    name: str
    arpu: float
    new_cust_start: int
    growth_rate: float
    monthly_churn: float
    nrr: float
    series_a_amount: int = 0
    series_a_pre: int = 0
    series_a_month: int = 0
    bridge_amount: int = 0
    bridge_month: int = 0

SCENARIOS = [
    Scenario('Conservative', 165, 12, 0.08, 0.055, 1.05,
             bridge_amount=5_000_000, bridge_month=18),
    Scenario('Base', 195, 18, 0.12, 0.04, 1.15,
             series_a_amount=15_000_000, series_a_pre=80_000_000, series_a_month=25),
    Scenario('Optimistic', 230, 25, 0.15, 0.028, 1.25,
             series_a_amount=20_000_000, series_a_pre=120_000_000, series_a_month=25),
]

# Conservative gets a second bridge at M30
CONSERVATIVE_BRIDGE_2 = (30, 3_000_000)

# Headcount scaling targets by scenario (end of Y3/Y4/Y5)
HC_TARGETS = {
    'Conservative': [(36, 35), (48, 45), (60, 60)],
    'Base':         [(36, 80), (48, 120), (60, 150)],
    'Optimistic':   [(36, 90), (48, 140), (60, 180)],
}


# ═══════════════════════════════════════════════════════════════════
# MODEL ENGINE
# ═══════════════════════════════════════════════════════════════════

def month_label(m: int) -> str:
    """Month 1 = 2026-04."""
    y = 2026 + (m - 1 + 3) // 12
    mo = ((m - 1 + 3) % 12) + 1
    return f"{y}-{mo:02d}"


def build_acquisitions(sc: Scenario) -> List[int]:
    """Pre-compute monthly new customer counts with decelerating growth."""
    acq = []
    current = float(sc.new_cust_start)
    for m in range(1, TOTAL_MONTHS + 1):
        acq.append(max(1, min(round(current), 500)))  # hard cap at 500/mo
        # Growth deceleration schedule
        if m <= 18:
            rate = sc.growth_rate
        elif m <= 30:
            rate = min(sc.growth_rate, 0.08)
        elif m <= 42:
            rate = min(sc.growth_rate, 0.04)
        else:
            rate = min(sc.growth_rate, 0.02)
        current *= (1 + rate)
    return acq


def build_model(sc: Scenario) -> List[dict]:
    """Build 60-month financial model."""

    acquisitions = build_acquisitions(sc)

    def get_expansion_rate(month):
        """NRR tapers as customer base matures."""
        if month <= 24:
            nrr = sc.nrr
        elif month <= 36:
            # Taper toward 110% over Year 3
            nrr = sc.nrr + (1.10 - sc.nrr) * (month - 24) / 12
        else:
            # Steady state ~108% for mature SaaS
            nrr = 1.08
        factor = nrr ** (1 / 12)
        return factor / (1 - sc.monthly_churn) - 1

    # State
    customers = STARTING_CUSTOMERS
    mrr = float(STARTING_MRR)
    cash = float(STARTING_CASH)

    rd_hc_cost = CURRENT_RD_MONTHLY
    sm_hc_cost = CURRENT_SM_MONTHLY
    ga_hc_cost = CURRENT_GA_MONTHLY
    rd_n, sm_n, ga_n = 7, 5, 2
    hc = 14

    rows = []

    for m in range(1, TOTAL_MONTHS + 1):
        beg_cust = customers
        beg_mrr = mrr
        beg_cash = cash

        # ── ARPU for new customers ──
        new_arpu = CURRENT_ARPU if m < MIGRATION_START_MONTH else sc.arpu

        # ── Customers ──
        new_cust = acquisitions[m - 1]
        churned = max(1, round(beg_cust * sc.monthly_churn)) if beg_cust > 20 else round(beg_cust * sc.monthly_churn)
        price_churn = PRICING_CHURN_CUSTOMERS if m == MIGRATION_START_MONTH else 0
        total_churned = churned + price_churn
        customers = max(0, beg_cust + new_cust - total_churned)

        # ── MRR (cohort-based with NRR) ──
        # Existing MRR: apply logo-churn dollar loss + expansion on retained
        expansion_rate = get_expansion_rate(m)
        churned_mrr = beg_mrr * sc.monthly_churn
        price_churn_mrr = (beg_mrr / beg_cust * price_churn) if price_churn and beg_cust else 0
        retained_base = beg_mrr - churned_mrr - price_churn_mrr
        expansion_mrr = retained_base * expansion_rate
        retained_mrr = retained_base + expansion_mrr
        new_mrr_added = new_cust * new_arpu
        mrr = retained_mrr + new_mrr_added

        arr = mrr * 12
        revenue = mrr

        # ── COGS ──
        cogs = revenue * COGS_PCT
        gross_profit = revenue - cogs
        gm = gross_profit / revenue if revenue > 0 else 0

        # ── Hiring ──
        # Conservative defers Y2 hires (M13-24) — cost discipline on slow growth
        max_plan_month = 12 if sc.name == 'Conservative' else 24
        if m in HIRES_BY_MONTH and m <= max_plan_month:
            for role, dept, cnt in HIRES_BY_MONTH[m]:
                cost_mo = ROLE_COST[role] / 12 * cnt
                if dept == 'R&D':
                    rd_hc_cost += cost_mo; rd_n += cnt
                elif dept == 'S&M':
                    sm_hc_cost += cost_mo; sm_n += cnt
                else:
                    ga_hc_cost += cost_mo; ga_n += cnt
                hc += cnt
        elif m > 24:
            # Capture actual Y2-end headcount once as scaling baseline
            if m == 25:
                _hc_y2 = hc
            targets = HC_TARGETS[sc.name]
            # Interpolate between target milestones
            if m <= targets[0][0]:
                target = _hc_y2 + (targets[0][1] - _hc_y2) * (m - 24) / (targets[0][0] - 24)
            elif m <= targets[1][0]:
                target = targets[0][1] + (targets[1][1] - targets[0][1]) * (m - targets[0][0]) / (targets[1][0] - targets[0][0])
            else:
                target = targets[1][1] + (targets[2][1] - targets[1][1]) * (m - targets[1][0]) / (targets[2][0] - targets[1][0])
            new_hires = max(0, round(target) - hc)
            if new_hires > 0:
                rd_add = round(new_hires * 0.45)
                sm_add = round(new_hires * 0.35)
                ga_add = new_hires - rd_add - sm_add
                rd_hc_cost += rd_add * ROLE_COST['engineer'] / 12
                sm_hc_cost += sm_add * (ROLE_COST['sales_rep'] * 0.5 + ROLE_COST['cs'] * 0.3 + ROLE_COST['marketing'] * 0.2) / 12
                ga_hc_cost += ga_add * ROLE_COST['hr_ops'] / 12
                rd_n += rd_add; sm_n += sm_add; ga_n += ga_add
                hc += new_hires

        # ── Non-HC OpEx ──
        # Marketing spend ramp
        if m <= 24:
            mktg = SM_MKTG_START + (SM_MKTG_Y2_END - SM_MKTG_START) * m / 24
        else:
            mktg = SM_MKTG_Y2_END * (1 + 0.15 * (m - 24) / 12)

        # G&A overhead ramp
        if m <= 36:
            ga_oh = GA_OVERHEAD_START + (GA_OVERHEAD_Y3 - GA_OVERHEAD_START) * m / 36
        else:
            ga_oh = GA_OVERHEAD_Y3 * (1 + 0.10 * (m - 36) / 12)

        sm_total = sm_hc_cost + mktg
        rd_total = rd_hc_cost + RD_TOOLING
        ga_total = ga_hc_cost + ga_oh
        total_opex = cogs + sm_total + rd_total + ga_total

        # ── Cash flow ──
        net = revenue - total_opex
        funding = 0
        if m == SEED_MONTH:
            funding += SEED_AMOUNT
        if sc.series_a_month and m == sc.series_a_month:
            funding += sc.series_a_amount
        if sc.bridge_month and m == sc.bridge_month:
            funding += sc.bridge_amount
        if sc.name == 'Conservative' and m == CONSERVATIVE_BRIDGE_2[0]:
            funding += CONSERVATIVE_BRIDGE_2[1]

        cash = beg_cash + net + funding
        burn = -net if net < 0 else 0
        runway = cash / burn if burn > 0 else float('inf')

        rows.append({
            'month': m, 'label': month_label(m),
            'beg_cust': beg_cust, 'new_cust': new_cust,
            'churned': total_churned, 'end_cust': customers,
            'arpu': new_arpu, 'beg_mrr': beg_mrr,
            'new_mrr': new_mrr_added, 'expansion_mrr': expansion_mrr,
            'churned_mrr': churned_mrr + price_churn_mrr,
            'end_mrr': mrr, 'arr': arr,
            'revenue': revenue, 'cogs': cogs,
            'gross_profit': gross_profit, 'gm': gm,
            'sm': sm_total, 'rd': rd_total, 'ga': ga_total,
            'total_opex': total_opex, 'net': net,
            'funding': funding, 'beg_cash': beg_cash, 'end_cash': cash,
            'burn': burn, 'runway': runway,
            'hc': hc, 'rd_n': rd_n, 'sm_n': sm_n, 'ga_n': ga_n,
        })

    return rows


# ═══════════════════════════════════════════════════════════════════
# OUTPUT — CSV
# ═══════════════════════════════════════════════════════════════════

def write_csv(rows, name, out):
    path = os.path.join(out, f"model_{name.lower()}.csv")
    with open(path, 'w', newline='') as f:
        w = csv.writer(f)
        w.writerow([
            'Month', 'Period', 'Beg Customers', 'New Customers', 'Churned',
            'End Customers', 'New ARPU', 'MRR', 'ARR', 'Revenue',
            'COGS', 'Gross Profit', 'Gross Margin',
            'S&M', 'R&D', 'G&A', 'Total OpEx',
            'Net Income', 'Funding', 'Beg Cash', 'End Cash',
            'Monthly Burn', 'Runway Months',
            'Headcount', 'R&D HC', 'S&M HC', 'G&A HC',
        ])
        for r in rows:
            w.writerow([
                r['month'], r['label'],
                r['beg_cust'], r['new_cust'], r['churned'], r['end_cust'],
                f"{r['arpu']:.0f}",
                f"{r['end_mrr']:.0f}", f"{r['arr']:.0f}", f"{r['revenue']:.0f}",
                f"{r['cogs']:.0f}", f"{r['gross_profit']:.0f}",
                f"{r['gm']*100:.1f}%",
                f"{r['sm']:.0f}", f"{r['rd']:.0f}", f"{r['ga']:.0f}",
                f"{r['total_opex']:.0f}", f"{r['net']:.0f}",
                f"{r['funding']:.0f}", f"{r['beg_cash']:.0f}",
                f"{r['end_cash']:.0f}", f"{r['burn']:.0f}",
                f"{r['runway']:.1f}" if r['runway'] < 1e6 else "n/a",
                r['hc'], r['rd_n'], r['sm_n'], r['ga_n'],
            ])
    return path


# ═══════════════════════════════════════════════════════════════════
# OUTPUT — SUMMARY MARKDOWN
# ═══════════════════════════════════════════════════════════════════

def fc(v):
    """Format currency."""
    if abs(v) >= 1_000_000:
        return f"${v/1e6:.1f}M"
    if abs(v) >= 1_000:
        return f"${v/1e3:.0f}K"
    return f"${v:.0f}"


def yr_sum(rows, key, s, e):
    return sum(rows[i][key] for i in range(s, e))


def generate_summary(results, out):
    L = []
    a = L.append

    a("# Doany.ai — Seed Financial Model\n")
    a("*Refreshed April 10, 2026 | Investor Review Copy*\n")
    a("---\n")

    # ── Overview ──
    a("## Company Snapshot\n")
    a("| | |")
    a("|--|--|")
    a("| **Product** | AI-powered workflow automation SaaS |")
    a("| **Founded / Launched** | Oct 2025 / Jan 2026 |")
    a("| **Pre-seed** | $1.8M at $9M post-money (Aug 2025) |")
    a("| **Current state** | 58 customers, $8.4K MRR, 14 employees |")
    a("| **Cash remaining** | $1.22M |")
    a("| **Seed ask** | **$5M at $25M pre-money (16.7% dilution)** |")
    a("")

    # ── New pricing ──
    a("## New Pricing (effective May 2026)\n")
    a("| Tier | Monthly | Users | Notes |")
    a("|------|---------|-------|-------|")
    a("| Starter | $79 | Up to 5 | Was $49 |")
    a("| Growth | $199 | Up to 20 | Was Pro $149 |")
    a("| Scale | $499 | Up to 50 | **New tier** |")
    a("| Enterprise | $1,500+ | Custom | Floor raised |")
    a("")

    # ── 5-Year Summary Table ──
    a("## Five-Year Scenario Overview\n")
    YR = [(0,12), (12,24), (24,36), (36,48), (48,60)]

    for name, rows in results:
        a(f"### {name}\n")
        ms = [rows[e-1] for _, e in YR]
        a("| Metric | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 |")
        a("|--------|--------|--------|--------|--------|--------|")
        a(f"| **ARR** | {fc(ms[0]['arr'])} | {fc(ms[1]['arr'])} | {fc(ms[2]['arr'])} | {fc(ms[3]['arr'])} | {fc(ms[4]['arr'])} |")
        a(f"| MRR | {fc(ms[0]['end_mrr'])} | {fc(ms[1]['end_mrr'])} | {fc(ms[2]['end_mrr'])} | {fc(ms[3]['end_mrr'])} | {fc(ms[4]['end_mrr'])} |")
        a(f"| Customers | {ms[0]['end_cust']:,} | {ms[1]['end_cust']:,} | {ms[2]['end_cust']:,} | {ms[3]['end_cust']:,} | {ms[4]['end_cust']:,} |")
        a(f"| Headcount | {ms[0]['hc']} | {ms[1]['hc']} | {ms[2]['hc']} | {ms[3]['hc']} | {ms[4]['hc']} |")
        a(f"| Monthly Burn | {fc(ms[0]['burn'])} | {fc(ms[1]['burn'])} | {fc(ms[2]['burn'])} | {fc(ms[3]['burn'])} | {fc(ms[4]['burn'])} |")
        a(f"| Cash | {fc(ms[0]['end_cash'])} | {fc(ms[1]['end_cash'])} | {fc(ms[2]['end_cash'])} | {fc(ms[3]['end_cash'])} | {fc(ms[4]['end_cash'])} |")
        a(f"| Gross Margin | {ms[0]['gm']*100:.0f}% | {ms[1]['gm']*100:.0f}% | {ms[2]['gm']*100:.0f}% | {ms[3]['gm']*100:.0f}% | {ms[4]['gm']*100:.0f}% |")
        rev = [yr_sum(rows, 'revenue', s, e) for s, e in YR]
        a(f"| Total Revenue | {fc(rev[0])} | {fc(rev[1])} | {fc(rev[2])} | {fc(rev[3])} | {fc(rev[4])} |")
        a("")

    # ── Base case P&L ──
    base = results[1][1]
    a("## Annual P&L — Base Case\n")
    a("| | Year 1 | Year 2 | Year 3 | Year 4 | Year 5 |")
    a("|--|--------|--------|--------|--------|--------|")
    for label, key in [("**Revenue**", 'revenue'), ("COGS", 'cogs'), ("**Gross Profit**", 'gross_profit'),
                       ("S&M", 'sm'), ("R&D", 'rd'), ("G&A", 'ga'),
                       ("**Total OpEx**", 'total_opex'), ("**Net Income**", 'net')]:
        vals = [yr_sum(base, key, s, e) for s, e in YR]
        if key in ('cogs', 'sm', 'rd', 'ga', 'total_opex'):
            a(f"| {label} | " + " | ".join(f"({fc(v)})" for v in vals) + " |")
        else:
            a(f"| {label} | " + " | ".join(fc(v) for v in vals) + " |")
    a("")

    # ── Year 1 monthly build (Base) ──
    a("## Year 1 Monthly Detail — Base Case\n")
    a("| Month | New | Churn | Cust | MRR | ARR | Revenue | OpEx | Net | Cash |")
    a("|-------|-----|-------|------|-----|-----|---------|------|-----|------|")
    for r in base[:12]:
        a(f"| {r['label']} | {r['new_cust']} | {r['churned']} | {r['end_cust']} "
          f"| {fc(r['end_mrr'])} | {fc(r['arr'])} | {fc(r['revenue'])} "
          f"| {fc(r['total_opex'])} | {fc(r['net'])} | {fc(r['end_cash'])} |")
    a("")

    # ── Year 2 monthly (Base) ──
    a("## Year 2 Monthly Detail — Base Case\n")
    a("| Month | New | Churn | Cust | MRR | ARR | Revenue | OpEx | Net | Cash |")
    a("|-------|-----|-------|------|-----|-----|---------|------|-----|------|")
    for r in base[12:24]:
        a(f"| {r['label']} | {r['new_cust']} | {r['churned']} | {r['end_cust']} "
          f"| {fc(r['end_mrr'])} | {fc(r['arr'])} | {fc(r['revenue'])} "
          f"| {fc(r['total_opex'])} | {fc(r['net'])} | {fc(r['end_cash'])} |")
    a("")

    # ── Year 3 quarterly (Base) ──
    a("## Year 3 Quarterly — Base Case\n")
    a("| Quarter | End Cust | MRR | ARR | Revenue | OpEx | Net | Cash |")
    a("|---------|----------|-----|-----|---------|------|-----|------|")
    for q in range(4):
        s, e = 24 + q*3, 24 + (q+1)*3
        qr = base[e-1]
        a(f"| Q{q+1} FY3 | {qr['end_cust']:,} | {fc(qr['end_mrr'])} | {fc(qr['arr'])} "
          f"| {fc(yr_sum(base, 'revenue', s, e))} | {fc(yr_sum(base, 'total_opex', s, e))} "
          f"| {fc(yr_sum(base, 'net', s, e))} | {fc(qr['end_cash'])} |")
    a("")

    # ── Years 4-5 annual (Base) ──
    a("## Years 4–5 Annual — Base Case\n")
    a("| Metric | Year 4 | Year 5 |")
    a("|--------|--------|--------|")
    y4, y5 = base[47], base[59]
    a(f"| Customers | {y4['end_cust']:,} | {y5['end_cust']:,} |")
    a(f"| ARR | {fc(y4['arr'])} | {fc(y5['arr'])} |")
    a(f"| Total Revenue | {fc(yr_sum(base,'revenue',36,48))} | {fc(yr_sum(base,'revenue',48,60))} |")
    a(f"| Total OpEx | {fc(yr_sum(base,'total_opex',36,48))} | {fc(yr_sum(base,'total_opex',48,60))} |")
    a(f"| Net Income | {fc(yr_sum(base,'net',36,48))} | {fc(yr_sum(base,'net',48,60))} |")
    a(f"| Headcount | {y4['hc']} | {y5['hc']} |")
    a(f"| Cash | {fc(y4['end_cash'])} | {fc(y5['end_cash'])} |")
    a("")

    # ── Unit economics (Base, Y1 end) ──
    a("## Unit Economics — Base Case (Year 1 End)\n")
    y1 = base[11]
    t3_sm = sum(base[i]['sm'] for i in range(9, 12))
    t3_new = sum(base[i]['new_cust'] for i in range(9, 12))
    cac = t3_sm / t3_new if t3_new else 0
    avg_life = 1 / 0.04  # 25 months
    ltv = y1['arpu'] * avg_life * (1 - COGS_PCT)
    payback = cac / (y1['arpu'] * (1 - COGS_PCT)) if y1['arpu'] else 0

    a("| Metric | Value |")
    a("|--------|-------|")
    a(f"| Blended ARPU | ${y1['arpu']:.0f}/mo |")
    a(f"| CAC (trailing 3-mo) | ${cac:,.0f} |")
    a(f"| Gross Margin | {y1['gm']*100:.0f}% |")
    a(f"| LTV (GM-adjusted) | ${ltv:,.0f} |")
    a(f"| LTV / CAC | {ltv/cac:.1f}x |")
    a(f"| CAC Payback | {payback:.1f} months |")
    a(f"| Monthly Logo Churn | 4.0% |")
    a(f"| Net Revenue Retention | 115% |")
    a("")

    # ── Efficiency metrics ──
    a("## Efficiency Metrics — Base Case\n")
    a("| Metric | Year 1 | Year 2 | Year 3 |")
    a("|--------|--------|--------|--------|")

    eff = []
    for s, e in YR[:3]:
        burn_total = -yr_sum(base, 'net', s, e)
        start_arr = base[s]['beg_mrr'] * 12
        end_arr = base[e-1]['arr']
        net_new = end_arr - start_arr
        bm = burn_total / net_new if net_new > 0 else 0
        rpe = base[e-1]['arr'] / base[e-1]['hc'] if base[e-1]['hc'] else 0
        rev = yr_sum(base, 'revenue', s, e)
        sm_pct = yr_sum(base, 'sm', s, e) / rev * 100 if rev else 0
        rd_pct = yr_sum(base, 'rd', s, e) / rev * 100 if rev else 0
        eff.append((bm, rpe, sm_pct, rd_pct))

    a(f"| Burn Multiple | {eff[0][0]:.1f}x | {eff[1][0]:.1f}x | {eff[2][0]:.1f}x |")
    a(f"| ARR / Employee | {fc(eff[0][1])} | {fc(eff[1][1])} | {fc(eff[2][1])} |")
    a(f"| S&M % Revenue | {eff[0][2]:.0f}% | {eff[1][2]:.0f}% | {eff[2][2]:.0f}% |")
    a(f"| R&D % Revenue | {eff[0][3]:.0f}% | {eff[1][3]:.0f}% | {eff[2][3]:.0f}% |")
    a("")

    # ── Headcount plan ──
    a("## Headcount Plan — Base Case\n")
    a("| Period | R&D | S&M | G&A | Total |")
    a("|--------|-----|-----|-----|-------|")
    a("| Current (Apr 2026) | 7 | 5 | 2 | 14 |")
    for lbl, idx in [("End Y1", 11), ("End Y2", 23), ("End Y3", 35), ("End Y4", 47), ("End Y5", 59)]:
        r = base[idx]
        a(f"| {lbl} | {r['rd_n']} | {r['sm_n']} | {r['ga_n']} | {r['hc']} |")
    a("")

    a("### Detailed Hiring Timeline (Years 1–2)\n")
    a("| When | Roles | Dept | Running HC |")
    a("|------|-------|------|------------|")
    timeline = [
        ("Current", "14 FTEs", "—", 14),
        ("May 2026", "+2 Engineers", "R&D", 16),
        ("Jul 2026", "+1 PM", "R&D", 17),
        ("Aug 2026", "+1 Designer", "R&D", 18),
        ("Oct 2026", "+2 Engineers", "R&D", 20),
        ("Nov 2026", "+1 Sales Rep", "S&M", 21),
        ("Dec 2026", "+1 CS", "S&M", 22),
        ("Jan 2027", "+1 Eng Manager", "R&D", 23),
        ("Feb 2027", "+2 Sales Reps", "S&M", 25),
        ("Mar 2027", "+1 Marketing", "S&M", 26),
        ("Apr–May 2027", "+3 Eng, +1 Sales Mgr", "R&D/S&M", 30),
        ("Jun 2027", "+1 HR/Ops", "G&A", 31),
        ("Jul–Dec 2027", "+4 Eng, +3 Sales, +1 CS, +1 Finance", "Mixed", 40),
    ]
    for when, roles, dept, n in timeline:
        a(f"| {when} | {roles} | {dept} | {n} |")
    a("")

    # ── Cash & runway ──
    a("## Cash Position & Runway\n")
    a("| Scenario | Post-Seed | End Y1 | End Y2 | Next Round | End Y3 | End Y5 |")
    a("|----------|-----------|--------|--------|------------|--------|--------|")
    for name, rows in results:
        ps = rows[SEED_MONTH - 1]
        y1, y2, y3, y5 = rows[11], rows[23], rows[35], rows[59]
        if name == 'Conservative':
            nr = "$5M bridge M18 + $3M M30"
        elif name == 'Base':
            nr = "$15M Series A (Q2 '28)"
        else:
            nr = "$20M Series A (Q2 '28)"
        a(f"| {name} | {fc(ps['end_cash'])} | {fc(y1['end_cash'])} | {fc(y2['end_cash'])} | {nr} | {fc(y3['end_cash'])} | {fc(y5['end_cash'])} |")
    a("")

    # ── Fundraising ──
    a("## Fundraising Assumptions\n")
    a("### Seed (June 2026)\n")
    a("- **$5.0M** at $25M pre-money → $30M post-money (16.7% dilution)")
    a("- Use of funds: product (40%), GTM (35%), G&A (15%), buffer (10%)\n")
    a("### Follow-On Financing\n")
    a("| | Conservative | Base | Optimistic |")
    a("|--|-------------|------|------------|")
    a("| Type | Bridge rounds | Series A | Series A |")
    a("| Amount | $5M (M18) + $3M (M30) | $15M (Q2 '28) | $20M (Q2 '28) |")
    a("| Pre-money | — | $80M | $120M |")
    a("| Dilution | — | 15.8% | 14.3% |")
    a("| Notes | Cost discipline, deferred Y2 hires | Full hiring plan | Full hiring + accelerated scaling |")
    a("")

    # ── Assumptions table ──
    a("## Key Assumptions\n")
    a("| | Conservative | Base | Optimistic |")
    a("|--|-------------|------|------------|")
    a("| Blended ARPU | $165/mo | $195/mo | $230/mo |")
    a("| New cust start | 12/mo | 18/mo | 25/mo |")
    a("| Acq growth (M1-18) | 8% MoM | 12% MoM | 15% MoM |")
    a("| Acq growth (M19-30) | 8% | 8% | 8% |")
    a("| Acq growth (M31-42) | 4% | 4% | 4% |")
    a("| Acq growth (M43+) | 2% | 2% | 2% |")
    a("| Monthly logo churn | 5.5% | 4.0% | 2.8% |")
    a("| Net revenue retention | 105% | 115% | 125% |")
    a("| COGS | 18% | 18% | 18% |")
    a("| Benefits multiplier | 1.35x | 1.35x | 1.35x |")
    a("| Migration churn | 5 customers | 5 customers | 5 customers |")
    a("| NRR tapering | → 108% by Y4 | → 108% by Y4 | → 108% by Y4 |")
    a("| Y2 hiring | Deferred (cost discipline) | Full plan | Full plan |")
    a("| Y5 headcount target | 60 | 150 | 180 |")
    a("")

    # ── Changes from prior model ──
    a("## Changes from Prior Model (Feb 2026)\n")
    a("| Area | Old Model | New Model |")
    a("|------|-----------|-----------|")
    a("| Pricing | $49 / $149 / custom | $79 / $199 / $499 / $1,500+ |")
    a("| Revenue method | Flat ARPU | Cohort-based with expansion |")
    a("| Hiring | Aggressive, pre-seed constrained | Non-eng delayed 2 months |")
    a("| Growth calibration | Assumptions only | Calibrated to 6 months actuals |")
    a("| Scenarios | Single base case | Conservative / Base / Optimistic |")
    a("| Expansion revenue | Not modeled | Explicit via NRR |")
    a("| Scale tier | Not available | $499/mo mid-market tier |")
    a("")

    # ── Risks ──
    a("## Key Risks & Sensitivities\n")
    a("1. **Pricing migration** — 15% Starter churn assumed; actual churn depends on migration comms and perceived value")
    a("2. **NRR dependency** — expansion revenue critical to all scenarios; product must support clear upsell paths (Starter → Growth → Scale)")
    a("3. **Series A timing (Base)** — model shows cash dip before M25 close; early fundraising start recommended")
    a("4. **Conservative viability** — requires $8M in bridge financing and deferred hiring; company reaches near-breakeven only in Y5")
    a("5. **Enterprise sales cycle** — Scale/Enterprise tiers require longer cycles; blended ARPU ramp may lag projections")
    a("6. **Hiring execution** — 26 hires through Y2 (base/optimistic) requires strong recruiting pipeline")
    a("7. **Market timing** — AI workflow space heating up; window for differentiation narrowing\n")

    a("---\n")
    a("*Model generated April 10, 2026. Detailed monthly CSVs: `model_conservative.csv`, `model_base.csv`, `model_optimistic.csv`*")

    path = os.path.join(out, 'model_summary.md')
    with open(path, 'w') as f:
        f.write('\n'.join(L))
    return path


# ═══════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════

if __name__ == '__main__':
    out = os.path.dirname(os.path.abspath(__file__))
    results = []

    for sc in SCENARIOS:
        rows = build_model(sc)
        csv_path = write_csv(rows, sc.name, out)
        results.append((sc.name, rows))

        y1, y2, y3, y5 = rows[11], rows[23], rows[35], rows[59]
        print(f"\n{'='*60}")
        print(f"  {sc.name.upper()} CASE")
        print(f"{'='*60}")
        print(f"  Y1  {y1['end_cust']:>5} cust   {fc(y1['arr']):>8} ARR   {fc(y1['end_cash']):>8} cash   {y1['hc']:>3} HC")
        print(f"  Y2  {y2['end_cust']:>5} cust   {fc(y2['arr']):>8} ARR   {fc(y2['end_cash']):>8} cash   {y2['hc']:>3} HC")
        print(f"  Y3  {y3['end_cust']:>5} cust   {fc(y3['arr']):>8} ARR   {fc(y3['end_cash']):>8} cash   {y3['hc']:>3} HC")
        print(f"  Y5  {y5['end_cust']:>5} cust   {fc(y5['arr']):>8} ARR   {fc(y5['end_cash']):>8} cash   {y5['hc']:>3} HC")
        print(f"  CSV → {csv_path}")

    summary_path = generate_summary(results, out)
    print(f"\n{'='*60}")
    print(f"  SUMMARY → {summary_path}")
    print(f"{'='*60}")
