#!/usr/bin/env python3
"""Generate board-ready exec review deck for doany.ai Q2 growth review."""

from pptx import Presentation
from pptx.util import Inches, Pt, Emu
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.chart import XL_CHART_TYPE, XL_LEGEND_POSITION
from pptx.chart.data import CategoryChartData
import os
from PIL import Image, ImageDraw, ImageFilter
import math
import random

# --- Brand Colors ---
PRIMARY_BLUE = RGBColor(0x25, 0x63, 0xEB)
DARK_NAVY = RGBColor(0x0F, 0x17, 0x2A)
ACCENT_TEAL = RGBColor(0x14, 0xB8, 0xA6)
WARM_ORANGE = RGBColor(0xF9, 0x73, 0x16)
LIGHT_GRAY = RGBColor(0xF1, 0xF5, 0xF9)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
MID_GRAY = RGBColor(0x94, 0xA3, 0xB8)
DARK_GRAY = RGBColor(0x33, 0x40, 0x55)

SLIDE_WIDTH = Inches(13.333)
SLIDE_HEIGHT = Inches(7.5)

OUT_DIR = os.path.dirname(os.path.abspath(__file__))
IMG_DIR = os.path.join(OUT_DIR, "images")
os.makedirs(IMG_DIR, exist_ok=True)


# ---- Image generation helpers ----

def _gradient_bg(w, h, c1, c2):
    """Create a horizontal gradient image."""
    img = Image.new("RGB", (w, h))
    for x in range(w):
        r = int(c1[0] + (c2[0] - c1[0]) * x / w)
        g = int(c1[1] + (c2[1] - c1[1]) * x / w)
        b = int(c1[2] + (c2[2] - c1[2]) * x / w)
        for y in range(h):
            img.putpixel((x, y), (r, g, b))
    return img


def _draw_grid(draw, w, h, spacing=80, color=(255, 255, 255, 18)):
    for x in range(0, w, spacing):
        draw.line([(x, 0), (x, h)], fill=color, width=1)
    for y in range(0, h, spacing):
        draw.line([(0, y), (w, y)], fill=color, width=1)


def _draw_nodes(draw, w, h, n=12, seed=42):
    random.seed(seed)
    pts = [(random.randint(40, w - 40), random.randint(40, h - 40)) for _ in range(n)]
    # connections
    for i, p1 in enumerate(pts):
        for p2 in pts[i + 1:]:
            dist = math.hypot(p1[0] - p2[0], p1[1] - p2[1])
            if dist < 300:
                draw.line([p1, p2], fill=(255, 255, 255, 25), width=1)
    # nodes
    for p in pts:
        r = random.randint(4, 8)
        draw.ellipse([p[0] - r, p[1] - r, p[0] + r, p[1] + r], fill=(20, 184, 166, 180))


def generate_hero_image(filename, w=1200, h=500, seed=42):
    """Abstract geometric hero: blue->teal gradient, grid, nodes."""
    base = _gradient_bg(w, h, (15, 23, 42), (20, 80, 120))
    overlay = Image.new("RGBA", (w, h), (0, 0, 0, 0))
    draw = ImageDraw.Draw(overlay)
    _draw_grid(draw, w, h)
    _draw_nodes(draw, w, h, n=18, seed=seed)
    # glow circles
    random.seed(seed + 1)
    for _ in range(5):
        cx, cy = random.randint(0, w), random.randint(0, h)
        rad = random.randint(60, 140)
        for ring in range(rad, 0, -2):
            alpha = int(12 * ring / rad)
            draw.ellipse([cx - ring, cy - ring, cx + ring, cy + ring],
                         fill=(37, 99, 235, alpha))
    base.paste(Image.alpha_composite(Image.new("RGBA", (w, h), (0, 0, 0, 0)), overlay),
               mask=overlay)
    path = os.path.join(IMG_DIR, filename)
    base.save(path, "PNG")
    return path


def generate_cover_image():
    return generate_hero_image("cover_hero.png", 1200, 500, seed=100)


def generate_section_image(name, seed):
    return generate_hero_image(f"{name}_hero.png", 1000, 300, seed=seed)


# ---- PPT helpers ----

def set_slide_bg(slide, color):
    bg = slide.background
    fill = bg.fill
    fill.solid()
    fill.fore_color.rgb = color


def add_shape_rect(slide, left, top, width, height, fill_color, corner_radius=None):
    shape = slide.shapes.add_shape(
        MSO_SHAPE.ROUNDED_RECTANGLE if corner_radius else MSO_SHAPE.RECTANGLE,
        left, top, width, height
    )
    shape.fill.solid()
    shape.fill.fore_color.rgb = fill_color
    shape.line.fill.background()
    return shape


def tf_set(tf, text, size=14, bold=False, color=DARK_NAVY, align=PP_ALIGN.LEFT):
    tf.clear()
    tf.word_wrap = True
    p = tf.paragraphs[0]
    p.text = text
    p.font.size = Pt(size)
    p.font.bold = bold
    p.font.color.rgb = color
    p.font.name = "Inter"
    p.alignment = align
    return p


def add_text_box(slide, left, top, width, height, text, size=14, bold=False,
                 color=DARK_NAVY, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP):
    tb = slide.shapes.add_textbox(left, top, width, height)
    tb.text_frame.word_wrap = True
    tf_set(tb.text_frame, text, size, bold, color, align)
    tb.text_frame.auto_size = None
    return tb


def add_para(tf, text, size=14, bold=False, color=DARK_NAVY, space_before=Pt(6), align=PP_ALIGN.LEFT):
    p = tf.add_paragraph()
    p.text = text
    p.font.size = Pt(size)
    p.font.bold = bold
    p.font.color.rgb = color
    p.font.name = "Inter"
    p.space_before = space_before
    p.alignment = align
    return p


def add_kpi_card(slide, left, top, width, height, label, value, delta=None,
                 delta_color=ACCENT_TEAL, bg=WHITE):
    card = add_shape_rect(slide, left, top, width, height, bg, corner_radius=True)
    # label
    add_text_box(slide, left + Inches(0.2), top + Inches(0.15), width - Inches(0.4), Inches(0.35),
                 label, size=11, color=MID_GRAY, bold=False)
    # value
    add_text_box(slide, left + Inches(0.2), top + Inches(0.45), width - Inches(0.4), Inches(0.5),
                 value, size=28, color=DARK_NAVY, bold=True)
    # delta
    if delta:
        add_text_box(slide, left + Inches(0.2), top + Inches(0.95), width - Inches(0.4), Inches(0.3),
                     delta, size=12, color=delta_color, bold=True)


def add_table_slide_data(slide, left, top, width, rows_data, col_widths=None):
    """rows_data: list of lists. First row = header."""
    n_rows = len(rows_data)
    n_cols = len(rows_data[0])
    table_shape = slide.shapes.add_table(n_rows, n_cols, left, top, width, Inches(0.4 * n_rows))
    table = table_shape.table

    if col_widths:
        for i, w in enumerate(col_widths):
            table.columns[i].width = w

    for r, row in enumerate(rows_data):
        for c, val in enumerate(row):
            cell = table.cell(r, c)
            cell.text = str(val)
            for para in cell.text_frame.paragraphs:
                para.font.size = Pt(11)
                para.font.name = "Inter"
                if r == 0:
                    para.font.bold = True
                    para.font.color.rgb = WHITE
                else:
                    para.font.color.rgb = DARK_NAVY
                para.alignment = PP_ALIGN.CENTER if c > 0 else PP_ALIGN.LEFT

            if r == 0:
                cell.fill.solid()
                cell.fill.fore_color.rgb = PRIMARY_BLUE
            elif r % 2 == 0:
                cell.fill.solid()
                cell.fill.fore_color.rgb = LIGHT_GRAY
            else:
                cell.fill.solid()
                cell.fill.fore_color.rgb = WHITE

    return table_shape


# ---- Slide builders ----

def build_cover(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])  # blank
    set_slide_bg(slide, DARK_NAVY)

    # hero image
    img_path = generate_cover_image()
    slide.shapes.add_picture(img_path, Inches(6.5), Inches(0.5), Inches(6.5), Inches(3.5))

    # Title
    add_text_box(slide, Inches(0.8), Inches(1.2), Inches(5.5), Inches(1.2),
                 "Q2 Executive Review", size=40, bold=True, color=WHITE)
    # Subtitle
    add_text_box(slide, Inches(0.8), Inches(2.5), Inches(5.5), Inches(0.6),
                 "Signup Funnel Recovery & Growth Strategy", size=20, color=ACCENT_TEAL)
    # Meta
    add_text_box(slide, Inches(0.8), Inches(3.4), Inches(5.5), Inches(0.4),
                 "April 10, 2026  |  Prepared for Executive Team", size=13, color=MID_GRAY)

    # Brand line
    add_shape_rect(slide, Inches(0.8), Inches(4.2), Inches(2), Inches(0.05), ACCENT_TEAL)

    # doany.ai wordmark
    add_text_box(slide, Inches(0.8), Inches(6.5), Inches(3), Inches(0.5),
                 "doany.ai", size=18, bold=True, color=PRIMARY_BLUE)


def build_agenda(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.5), Inches(6), Inches(0.6),
                 "Agenda", size=32, bold=True, color=DARK_NAVY)
    add_shape_rect(slide, Inches(0.8), Inches(1.15), Inches(1.5), Inches(0.04), PRIMARY_BLUE)

    items = [
        ("01", "Signup Dip", "18% decline root causes & financial impact"),
        ("02", "Experiment Results", "Three experiments, two wins, one revisit"),
        ("03", "Recovery Projection", "Path back to baseline and beyond"),
        ("04", "Q2 Growth Plan", "Five initiatives, targets & key dates"),
        ("05", "Risks & Ask", "Mitigations and board decisions needed"),
    ]

    for i, (num, title, desc) in enumerate(items):
        y = Inches(1.8) + Inches(1.0) * i
        # number circle
        add_shape_rect(slide, Inches(0.8), y, Inches(0.55), Inches(0.55), PRIMARY_BLUE, corner_radius=True)
        add_text_box(slide, Inches(0.8), y + Inches(0.05), Inches(0.55), Inches(0.45),
                     num, size=18, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        add_text_box(slide, Inches(1.6), y + Inches(0.02), Inches(4), Inches(0.3),
                     title, size=18, bold=True, color=DARK_NAVY)
        add_text_box(slide, Inches(1.6), y + Inches(0.35), Inches(5), Inches(0.3),
                     desc, size=12, color=MID_GRAY)


def build_section_divider(prs, number, title, subtitle):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, DARK_NAVY)

    img_path = generate_section_image(title.lower().replace(" ", "_"), seed=hash(title) % 1000)
    slide.shapes.add_picture(img_path, Inches(7), Inches(1.5), Inches(6), Inches(4))

    add_text_box(slide, Inches(0.8), Inches(2.0), Inches(1), Inches(0.6),
                 number, size=48, bold=True, color=ACCENT_TEAL)
    add_text_box(slide, Inches(0.8), Inches(2.8), Inches(5.5), Inches(0.8),
                 title, size=36, bold=True, color=WHITE)
    add_text_box(slide, Inches(0.8), Inches(3.7), Inches(5.5), Inches(0.5),
                 subtitle, size=16, color=MID_GRAY)
    add_shape_rect(slide, Inches(0.8), Inches(4.4), Inches(2), Inches(0.04), ACCENT_TEAL)


def build_signup_dip_overview(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Weekly Signups Dropped 18%", size=28, bold=True, color=DARK_NAVY)

    # Top-right KPI
    add_text_box(slide, Inches(9.5), Inches(0.3), Inches(3.5), Inches(0.3),
                 "Weekly Signups (Apr Wk1)", size=11, color=MID_GRAY, align=PP_ALIGN.RIGHT)
    add_text_box(slide, Inches(9.5), Inches(0.6), Inches(3.5), Inches(0.5),
                 "10,150", size=36, bold=True, color=WARM_ORANGE, align=PP_ALIGN.RIGHT)
    add_text_box(slide, Inches(9.5), Inches(1.1), Inches(3.5), Inches(0.3),
                 "-18% vs. Feb baseline (12,400)", size=12, color=WARM_ORANGE, align=PP_ALIGN.RIGHT)

    # KPI cards row
    cards = [
        ("Q1 Signups", "147,200", "-3.2% vs target", WARM_ORANGE),
        ("Signup-to-Activation", "34%", "-4pp vs target (38%)", WARM_ORANGE),
        ("New User MRR / mo", "$152K", "-$34K vs Feb ($186K)", WARM_ORANGE),
        ("90-Day LTV / cohort", "$1.97M", "-$430K vs Feb ($2.4M)", WARM_ORANGE),
    ]
    for i, (label, val, delta, dc) in enumerate(cards):
        add_kpi_card(slide, Inches(0.8 + 3.1 * i), Inches(1.8), Inches(2.8), Inches(1.3),
                     label, val, delta, dc, LIGHT_GRAY)

    # Subtitle
    add_text_box(slide, Inches(0.8), Inches(3.5), Inches(11), Inches(0.4),
                 "Three compounding factors drove the decline:", size=14, bold=True, color=DARK_NAVY)

    # Root cause breakdown
    causes = [
        ("~40%", "Pricing Page Redesign", "CTA click-through dropped 23%; bounce rate rose 31% \u2192 47%;\nmobile CTA pushed below the fold", WARM_ORANGE),
        ("~35%", "Email Verification Gate", "12% of users never returned after verify click;\nmobile completion only 71% vs 94% desktop", WARM_ORANGE),
        ("~25%", "Seasonal SMB Softness", "Consistent with 5\u20138% historical dip;\nexpected to self-correct mid-April", MID_GRAY),
    ]
    for i, (pct, title, desc, accent) in enumerate(causes):
        x = Inches(0.8 + 4.1 * i)
        y = Inches(4.1)
        # pct badge
        add_shape_rect(slide, x, y, Inches(0.9), Inches(0.45), accent, corner_radius=True)
        add_text_box(slide, x, y + Inches(0.05), Inches(0.9), Inches(0.35),
                     pct, size=16, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        add_text_box(slide, x + Inches(1.0), y + Inches(0.05), Inches(2.8), Inches(0.35),
                     title, size=15, bold=True, color=DARK_NAVY)
        tb = add_text_box(slide, x, y + Inches(0.6), Inches(3.8), Inches(1.2),
                          desc, size=11, color=DARK_GRAY)


def build_q1_channel_performance(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Q1 Channel Performance", size=28, bold=True, color=DARK_NAVY)
    add_text_box(slide, Inches(9), Inches(0.4), Inches(4), Inches(0.3),
                 "Referral: highest conv. at 8.3%", size=12, bold=True, color=ACCENT_TEAL,
                 align=PP_ALIGN.RIGHT)

    rows = [
        ["Channel", "Signups", "CAC", "Conv. Rate", "Trend"],
        ["Organic Search", "52,100", "$0", "4.2%", "Stable"],
        ["Paid Search (Google)", "31,400", "$38", "3.1%", "Declining \u2014 CPC +15%"],
        ["Content / Blog", "24,800", "$12", "5.7%", "Growing \u2014 3 viral posts"],
        ["Product Hunt / Dirs", "11,200", "$0", "6.1%", "Spike from Feb launch"],
        ["Referral Program", "15,900", "$8", "8.3%", "Strongest by conv."],
        ["Social (LinkedIn, X)", "11,800", "$22", "2.4%", "Flat"],
    ]
    col_widths = [Inches(2.8), Inches(1.5), Inches(1.2), Inches(1.5), Inches(3.5)]
    add_table_slide_data(slide, Inches(0.8), Inches(1.3), Inches(11.5), rows, col_widths)

    # Callout
    add_shape_rect(slide, Inches(0.8), Inches(5.6), Inches(11.5), Inches(1.0), LIGHT_GRAY, corner_radius=True)
    tb = add_text_box(slide, Inches(1.1), Inches(5.75), Inches(10.8), Inches(0.7),
                      "Key Insight", size=13, bold=True, color=PRIMARY_BLUE)
    add_para(tb.text_frame,
             "Referral is our highest-converting channel (8.3%) but only 11% of volume. "
             "Q2 priority: scale referral from 11% to 18% of signups without diluting quality.",
             size=12, color=DARK_GRAY)


def build_experiment_results(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Experiment Results", size=28, bold=True, color=DARK_NAVY)
    add_text_box(slide, Inches(9), Inches(0.4), Inches(4), Inches(0.3),
                 "2 of 3 experiments hit significance", size=12, bold=True, color=ACCENT_TEAL,
                 align=PP_ALIGN.RIGHT)

    experiments = [
        {
            "letter": "A",
            "name": "Pricing Toggle",
            "dates": "Mar 20 \u2013 Apr 2",
            "result": "+14%",
            "metric": "signup conversion",
            "pval": "p = 0.003",
            "n": "n = 8,200/arm",
            "verdict": "SHIP",
            "verdict_color": ACCENT_TEAL,
            "detail": "Two-tab layout (Monthly/Annual) with sticky mobile CTA restored visibility. Ship to 100% immediately.",
            "bg": LIGHT_GRAY,
        },
        {
            "letter": "B",
            "name": "Delayed Verification",
            "dates": "Mar 24 \u2013 Apr 6",
            "result": "+9%",
            "metric": "signup-to-activation",
            "pval": "p = 0.018",
            "n": "n = 5,400/arm",
            "verdict": "SHIP",
            "verdict_color": ACCENT_TEAL,
            "detail": "Move verification to after first project creation. Fake accounts up only 4%. Ship with weekly abuse monitoring.",
            "bg": LIGHT_GRAY,
        },
        {
            "letter": "C",
            "name": "Social Proof Banner",
            "dates": "Mar 26 \u2013 Apr 6",
            "result": "+2.3%",
            "metric": "conversion lift",
            "pval": "p = 0.14",
            "n": "n = 6,100/arm",
            "verdict": "DO NOT SHIP",
            "verdict_color": WARM_ORANGE,
            "detail": "Not statistically significant. Revisit with customer quotes and case studies instead of logos. Re-test May.",
            "bg": WHITE,
        },
    ]

    for i, exp in enumerate(experiments):
        x = Inches(0.8 + 4.1 * i)
        y = Inches(1.3)
        card_w = Inches(3.8)
        card_h = Inches(5.5)

        add_shape_rect(slide, x, y, card_w, card_h, exp["bg"], corner_radius=True)

        # Experiment letter badge
        badge_color = ACCENT_TEAL if exp["verdict"] == "SHIP" else MID_GRAY
        add_shape_rect(slide, x + Inches(0.2), y + Inches(0.2), Inches(0.5), Inches(0.5),
                       badge_color, corner_radius=True)
        add_text_box(slide, x + Inches(0.2), y + Inches(0.25), Inches(0.5), Inches(0.4),
                     exp["letter"], size=20, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

        # Name + dates
        add_text_box(slide, x + Inches(0.85), y + Inches(0.22), Inches(2.7), Inches(0.3),
                     exp["name"], size=16, bold=True, color=DARK_NAVY)
        add_text_box(slide, x + Inches(0.85), y + Inches(0.52), Inches(2.7), Inches(0.25),
                     exp["dates"], size=10, color=MID_GRAY)

        # Big result number
        add_text_box(slide, x + Inches(0.2), y + Inches(1.1), card_w - Inches(0.4), Inches(0.7),
                     exp["result"], size=42, bold=True, color=PRIMARY_BLUE, align=PP_ALIGN.CENTER)
        add_text_box(slide, x + Inches(0.2), y + Inches(1.8), card_w - Inches(0.4), Inches(0.3),
                     exp["metric"], size=12, color=MID_GRAY, align=PP_ALIGN.CENTER)

        # Stats
        add_text_box(slide, x + Inches(0.2), y + Inches(2.3), Inches(1.7), Inches(0.25),
                     exp["pval"], size=11, bold=True, color=DARK_GRAY, align=PP_ALIGN.CENTER)
        add_text_box(slide, x + Inches(2.0), y + Inches(2.3), Inches(1.6), Inches(0.25),
                     exp["n"], size=11, color=DARK_GRAY, align=PP_ALIGN.CENTER)

        # Verdict badge
        add_shape_rect(slide, x + Inches(0.5), y + Inches(2.8), Inches(2.8), Inches(0.4),
                       exp["verdict_color"], corner_radius=True)
        add_text_box(slide, x + Inches(0.5), y + Inches(2.82), Inches(2.8), Inches(0.36),
                     exp["verdict"], size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)

        # Detail text
        add_text_box(slide, x + Inches(0.2), y + Inches(3.5), card_w - Inches(0.4), Inches(1.8),
                     exp["detail"], size=11, color=DARK_GRAY)


def build_recovery_projection(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Recovery Projection", size=28, bold=True, color=DARK_NAVY)
    add_text_box(slide, Inches(9), Inches(0.4), Inches(4), Inches(0.3),
                 "Projected: 12,800/wk (+3% above baseline)", size=12, bold=True, color=ACCENT_TEAL,
                 align=PP_ALIGN.RIGHT)

    # Chart
    chart_data = CategoryChartData()
    chart_data.categories = ["Feb Baseline", "Mar 10", "Mar 17", "Mar 24", "Mar 31", "Apr 7", "After Fixes"]
    chart_data.add_series("Weekly Signups", [12400, 11800, 11200, 10600, 10400, 10150, 12800])

    chart_frame = slide.shapes.add_chart(
        XL_CHART_TYPE.COLUMN_CLUSTERED, Inches(0.8), Inches(1.3),
        Inches(7.5), Inches(4.5), chart_data
    )
    chart = chart_frame.chart
    chart.has_legend = False
    chart.style = 2

    plot = chart.plots[0]
    plot.gap_width = 80
    series = plot.series[0]
    series.format.fill.solid()
    series.format.fill.fore_color.rgb = PRIMARY_BLUE

    # Color the last bar teal
    pt = series.points[6]
    pt.format.fill.solid()
    pt.format.fill.fore_color.rgb = ACCENT_TEAL

    # Color the dip bars orange
    for idx in [3, 4, 5]:
        pt = series.points[idx]
        pt.format.fill.solid()
        pt.format.fill.fore_color.rgb = WARM_ORANGE

    # Value axis
    value_axis = chart.value_axis
    value_axis.minimum_scale = 8000
    value_axis.maximum_scale = 14000
    value_axis.major_gridlines.format.line.color.rgb = LIGHT_GRAY

    # Right-side financial impact table
    rows = [
        ["Metric", "Before (Feb)", "Current (Apr)", "Projected"],
        ["Weekly Signups", "12,400", "10,150", "12,800"],
        ["Monthly New MRR", "$186K", "$152K", "$192K"],
        ["90-Day LTV/Cohort", "$2.4M", "$1.97M", "$2.5M"],
    ]
    col_widths = [Inches(1.6), Inches(1.1), Inches(1.1), Inches(1.1)]
    add_table_slide_data(slide, Inches(8.8), Inches(1.3), Inches(4.5), rows, col_widths)

    # Callout
    add_shape_rect(slide, Inches(8.8), Inches(3.5), Inches(4.2), Inches(1.5), LIGHT_GRAY, corner_radius=True)
    tb = add_text_box(slide, Inches(9.0), Inches(3.65), Inches(3.8), Inches(1.2),
                      "Shipping Exp A + B together is projected to recover the full decline and add ~3% net growth above February baseline.",
                      size=12, bold=False, color=DARK_GRAY)


def build_q2_targets(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Q2 Growth Targets", size=28, bold=True, color=DARK_NAVY)

    # Big KPI cards
    targets = [
        ("Total Signups", "168K", "Stretch: 180K", ACCENT_TEAL),
        ("Net New MRR", "$840K", "Stretch: $920K", ACCENT_TEAL),
        ("Signup \u2192 Activation", "40%", "Stretch: 43%", ACCENT_TEAL),
        ("Logo Churn", "< 4.0%", "Stretch: < 3.5%", ACCENT_TEAL),
    ]

    for i, (label, val, stretch, dc) in enumerate(targets):
        x = Inches(0.8 + 3.1 * i)
        card = add_shape_rect(slide, x, Inches(1.3), Inches(2.8), Inches(1.6), DARK_NAVY, corner_radius=True)
        add_text_box(slide, x + Inches(0.2), Inches(1.45), Inches(2.4), Inches(0.3),
                     label, size=11, color=MID_GRAY)
        add_text_box(slide, x + Inches(0.2), Inches(1.8), Inches(2.4), Inches(0.6),
                     val, size=36, bold=True, color=WHITE, align=PP_ALIGN.LEFT)
        add_text_box(slide, x + Inches(0.2), Inches(2.4), Inches(2.4), Inches(0.3),
                     stretch, size=11, color=ACCENT_TEAL)

    # Q2 budget
    add_text_box(slide, Inches(0.8), Inches(3.3), Inches(11), Inches(0.4),
                 "Q2 Growth Budget: $420K", size=16, bold=True, color=DARK_NAVY)

    budget_items = [
        ["Category", "Budget", "Share"],
        ["Paid Ads", "$180K", "43%"],
        ["Content", "$85K", "20%"],
        ["Referral Rewards", "$60K", "14%"],
        ["Events", "$50K", "12%"],
        ["Tools & Infra", "$45K", "11%"],
    ]
    col_widths = [Inches(3), Inches(1.5), Inches(1.5)]
    add_table_slide_data(slide, Inches(0.8), Inches(3.9), Inches(6), budget_items, col_widths)


def build_q2_initiatives(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Q2 Growth Initiatives", size=28, bold=True, color=DARK_NAVY)

    initiatives = [
        ("1", "Funnel Recovery", "April",
         "Ship pricing toggle (Exp A) + delayed verification (Exp B). Recover ~2,250 signups/week.",
         PRIMARY_BLUE),
        ("2", "Referral 2.0", "Apr\u2013May",
         "Tiered rewards ($10/$25/$50). In-product prompts at 3 engagement moments. Scale 11% \u2192 18% of signups.",
         PRIMARY_BLUE),
        ("3", "Content-Led Growth", "May\u2013Jun",
         "12 skill guides targeting long-tail SEO. Interactive homepage demo. 4 newsletter partnerships.",
         PRIMARY_BLUE),
        ("4", "Mobile Overhaul", "May",
         "Reduce onboarding steps 7 \u2192 4. Progressive profiling. Close 31% mobile-desktop gap by 50%.",
         PRIMARY_BLUE),
        ("5", "Enterprise Expansion", "June",
         "14 pilots in progress. Target 4 closes (avg $48K ARR). Enterprise landing page + ROI calculator May 15.",
         PRIMARY_BLUE),
    ]

    for i, (num, title, timing, desc, color) in enumerate(initiatives):
        y = Inches(1.2) + Inches(1.15) * i
        # number
        add_shape_rect(slide, Inches(0.8), y, Inches(0.45), Inches(0.45), color, corner_radius=True)
        add_text_box(slide, Inches(0.8), y + Inches(0.05), Inches(0.45), Inches(0.35),
                     num, size=16, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        # title + timing
        add_text_box(slide, Inches(1.5), y, Inches(3), Inches(0.35),
                     title, size=16, bold=True, color=DARK_NAVY)
        add_text_box(slide, Inches(4.5), y + Inches(0.05), Inches(1.2), Inches(0.3),
                     timing, size=11, bold=True, color=PRIMARY_BLUE)
        # desc
        add_text_box(slide, Inches(1.5), y + Inches(0.38), Inches(10.5), Inches(0.65),
                     desc, size=11, color=DARK_GRAY)


def build_key_dates(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Key Dates & Milestones", size=28, bold=True, color=DARK_NAVY)

    dates = [
        ("Apr 11", "Ship pricing toggle (Exp A)", PRIMARY_BLUE),
        ("Apr 14", "Ship delayed verification (Exp B)", PRIMARY_BLUE),
        ("Apr 22", "Referral 2.0 beta launch", PRIMARY_BLUE),
        ("May 1", "First batch of skill guides published", ACCENT_TEAL),
        ("May 15", "Enterprise landing page + ROI calculator", ACCENT_TEAL),
        ("May 20", "Mobile redesign v1 ships", ACCENT_TEAL),
        ("Jun 1", "Interactive homepage demo A/B test", MID_GRAY),
        ("Jun 15", "Q2 mid-quarter review", MID_GRAY),
        ("Jun 30", "Q2 close; board deck due Jul 3", MID_GRAY),
    ]

    # timeline line
    add_shape_rect(slide, Inches(2.2), Inches(1.3), Inches(0.04), Inches(5.5), LIGHT_GRAY)

    for i, (date, label, color) in enumerate(dates):
        y = Inches(1.3) + Inches(0.6) * i
        # dot
        dot = slide.shapes.add_shape(MSO_SHAPE.OVAL, Inches(2.08), y + Inches(0.05), Inches(0.2), Inches(0.2))
        dot.fill.solid()
        dot.fill.fore_color.rgb = color
        dot.line.fill.background()
        # date
        add_text_box(slide, Inches(0.5), y, Inches(1.5), Inches(0.3),
                     date, size=13, bold=True, color=color, align=PP_ALIGN.RIGHT)
        # label
        add_text_box(slide, Inches(2.6), y, Inches(8), Inches(0.3),
                     label, size=13, color=DARK_NAVY)


def build_risks(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Risks & Mitigations", size=28, bold=True, color=DARK_NAVY)

    rows = [
        ["Risk", "Likelihood", "Impact", "Mitigation"],
        ["Google CPC continues rising", "High", "Medium", "Shift 20% of paid budget to content/referral"],
        ["Enterprise deals slip to Q3", "Medium", "High", "Accelerate 2 pilots with dedicated onboarding"],
        ["Referral abuse (fake accounts)", "Medium", "Low", "Device fingerprinting + 30-day activation req."],
        ["Competitor launches free tier", "Low", "High", "Response playbook ready; accelerate differentiation"],
    ]
    col_widths = [Inches(3), Inches(1.3), Inches(1.3), Inches(5.5)]
    add_table_slide_data(slide, Inches(0.8), Inches(1.2), Inches(11.5), rows, col_widths)


def build_next_steps(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, WHITE)

    add_text_box(slide, Inches(0.8), Inches(0.4), Inches(7), Inches(0.5),
                 "Recommended Next Steps", size=28, bold=True, color=DARK_NAVY)

    steps = [
        ("Ship Experiment A (pricing toggle)", "April 11", "Immediate: restore CTA visibility, recover pricing page conversion"),
        ("Ship Experiment B (delayed verification)", "April 14", "With abuse monitoring dashboard; expected +9% activation lift"),
        ("Redesign social proof section", "May re-test", "Customer quotes + case studies replacing logo banner"),
        ("Audit mobile signup flow end-to-end", "May 20", "Mobile conversion is 31% lower than desktop \u2014 close gap by 50%"),
        ("Launch SMB re-engagement campaign", "Mid-April", "Target budget-cycle reset; seasonal softness expected to self-correct"),
    ]

    for i, (step, date, detail) in enumerate(steps):
        y = Inches(1.3) + Inches(1.1) * i
        # checkbox-style
        cb = add_shape_rect(slide, Inches(0.8), y + Inches(0.05), Inches(0.35), Inches(0.35),
                            PRIMARY_BLUE, corner_radius=True)
        add_text_box(slide, Inches(0.8), y + Inches(0.05), Inches(0.35), Inches(0.35),
                     str(i + 1), size=14, bold=True, color=WHITE, align=PP_ALIGN.CENTER)
        add_text_box(slide, Inches(1.4), y, Inches(7), Inches(0.35),
                     step, size=15, bold=True, color=DARK_NAVY)
        # date badge
        add_shape_rect(slide, Inches(9), y, Inches(1.8), Inches(0.35), LIGHT_GRAY, corner_radius=True)
        add_text_box(slide, Inches(9), y + Inches(0.02), Inches(1.8), Inches(0.3),
                     date, size=11, bold=True, color=PRIMARY_BLUE, align=PP_ALIGN.CENTER)
        add_text_box(slide, Inches(1.4), y + Inches(0.4), Inches(10), Inches(0.5),
                     detail, size=11, color=DARK_GRAY)


def build_closing(prs):
    slide = prs.slides.add_slide(prs.slide_layouts[6])
    set_slide_bg(slide, DARK_NAVY)

    img_path = generate_section_image("closing", seed=999)
    slide.shapes.add_picture(img_path, Inches(6.5), Inches(1), Inches(6.5), Inches(5))

    add_text_box(slide, Inches(0.8), Inches(2.2), Inches(5.5), Inches(0.8),
                 "Discussion", size=40, bold=True, color=WHITE)
    add_text_box(slide, Inches(0.8), Inches(3.2), Inches(5.5), Inches(0.5),
                 "Q2 Exec Review  |  April 10, 2026", size=16, color=MID_GRAY)
    add_shape_rect(slide, Inches(0.8), Inches(4.0), Inches(2), Inches(0.04), ACCENT_TEAL)
    add_text_box(slide, Inches(0.8), Inches(6.5), Inches(3), Inches(0.5),
                 "doany.ai", size=18, bold=True, color=PRIMARY_BLUE)


# ---- Main ----

def main():
    prs = Presentation()
    prs.slide_width = SLIDE_WIDTH
    prs.slide_height = SLIDE_HEIGHT

    # Build all slides
    build_cover(prs)                       # 1  - Title
    build_agenda(prs)                      # 2  - Agenda
    build_section_divider(prs, "01",       # 3  - Section: Signup Dip
        "Signup Dip", "Root causes and financial impact")
    build_signup_dip_overview(prs)         # 4  - Signup decline details
    build_q1_channel_performance(prs)      # 5  - Channel performance
    build_section_divider(prs, "02",       # 6  - Section: Experiments
        "Experiment Results", "Three tests, two wins")
    build_experiment_results(prs)          # 7  - Experiment cards
    build_recovery_projection(prs)         # 8  - Recovery chart + financials
    build_section_divider(prs, "03",       # 9  - Section: Next Steps
        "Q2 Growth Plan", "Initiatives, targets & roadmap")
    build_q2_targets(prs)                  # 10 - Q2 targets + budget
    build_q2_initiatives(prs)              # 11 - Five initiatives
    build_key_dates(prs)                   # 12 - Timeline
    build_risks(prs)                       # 13 - Risk matrix
    build_next_steps(prs)                  # 14 - Recommended actions
    build_closing(prs)                     # 15 - Discussion / close

    out_path = os.path.join(OUT_DIR, "Q2_Exec_Review_Apr10.pptx")
    prs.save(out_path)
    print(f"Deck saved to: {out_path}")
    print(f"Total slides: {len(prs.slides)}")


if __name__ == "__main__":
    main()
