File size: 8,426 Bytes
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
889e86b
e561008
 
89a70e2
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
020705e
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
020705e
e561008
 
 
 
 
 
889e86b
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89a70e2
 
e561008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
"""
Document Content Generation Route
──────────────────────────────────────────────────────────────────────────────
Generates structured content for Business Plan and Pitch Deck using Groq
with full key rotation. The generated content is saved to Firestore and
returned to the frontend for client-side PDF rendering via jsPDF.

Profile-based documents (BMC, Identity, Customer, Market) do NOT use this
endpoint β€” they generate PDF directly from profile_output in the frontend.

This endpoint handles only the documents that require AI narrative generation:
  - business_plan  β†’ 9-section written plan
  - pitch_deck     β†’ 7-slide pitch outline
"""

import json
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from firebase_admin import firestore
from utils.rate_limiter import with_rate_limit
from utils.groq_client import call_groq_json, HEAVY_MODEL
from utils.pinecone_client import query_book_context
from utils import business_type

router = APIRouter()


class GenerateDocRequest(BaseModel):
    userId:     str
    businessId: str
    docType:    str  # "business_plan" | "pitch_deck"


def _get_bff_and_profile(user_id: str, business_id: str) -> dict:
    """Reads BFF sections + profile_output from Firestore into one context dict."""
    db  = firestore.client()
    biz = (
        db.collection("user_data")
        .document(user_id)
        .collection("businesses")
        .document(business_id)
    )

    context: dict = {}

    # BFF sections
    for section in ["section1", "section2", "section3"]:
        doc = biz.collection("bff").document(section).get()
        if doc.exists:
            context[section] = doc.to_dict()

    # Profile outputs
    for key in ["businessIdentity", "customerProfile", "marketData", "businessModel"]:
        doc = biz.collection("profile_output").document(key).get()
        if doc.exists:
            context[key] = doc.to_dict()

    return context


# ─── Business Plan ────────────────────────────────────────────────────────────
def _generate_business_plan(ctx: dict, book_context: str) -> dict:
    system = (
        "You are a professional business consultant specializing in Nigerian MSMEs. "
        "Write a complete, practical Business Plan for this entrepreneur. "
        "Use plain English β€” no jargon. Every section must be actionable and specific "
        "to the Nigerian market. Return ONLY valid JSON.\n\n"
        + (book_context or "")
    )
    prompt = f"""
Business context:
{json.dumps(ctx, indent=2)[:3000]}

Generate a comprehensive Business Plan. Return JSON:
{{
  "executiveSummary": "2-3 paragraphs summarizing the business, opportunity, and goals",
  "businessOpportunity": "The problem being solved and why now",
  "companyDescription": "What the business does, its mission, and legal structure",
  "targetMarket": "Detailed description of the target market in Nigeria",
  "productsServices": "What is being sold, key features, pricing approach",
  "marketingStrategy": "How the business will reach and retain customers",
  "operationsPlan": "Day-to-day operations, location, equipment, team",
  "financialSummary": "Startup costs, expected revenue, break-even estimate",
  "implementationTimeline": "Key milestones for the first 12 months"
}}"""
    return call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=2500, fixture="business_plan")


# ─── Pitch Deck ────────────────────────────────────────────────────────────────
def _generate_pitch_deck(ctx: dict, book_context: str) -> dict:
    system = (
        "You are a venture capital pitch consultant. "
        "Create a compelling 7-slide investor pitch deck for this Nigerian entrepreneur. "
        "Be concise β€” each slide has 3-4 punchy bullet points. "
        "Ground everything in real Nigerian market data where possible. "
        "Return ONLY valid JSON.\n\n"
        + (book_context or "")
    )
    prompt = f"""
Business context:
{json.dumps(ctx, indent=2)[:3000]}

Generate a 7-slide Pitch Deck. Return JSON where each key is a slide:
{{
  "slide1_problem": {{
    "title": "The Problem",
    "bullets": ["...", "...", "..."]
  }},
  "slide2_solution": {{
    "title": "Our Solution",
    "bullets": ["...", "...", "..."]
  }},
  "slide3_market": {{
    "title": "Market Opportunity",
    "bullets": ["TAM...", "SAM...", "SOM..."]
  }},
  "slide4_business_model": {{
    "title": "How We Make Money",
    "bullets": ["...", "...", "..."]
  }},
  "slide5_traction": {{
    "title": "Traction & Milestones",
    "bullets": ["...", "...", "..."]
  }},
  "slide6_strategy": {{
    "title": "Go-To-Market Strategy",
    "bullets": ["...", "...", "..."]
  }},
  "slide7_ask": {{
    "title": "The Ask",
    "bullets": ["Seeking: ...", "Use of funds: ...", "Expected outcome: ..."]
  }}
}}"""
    return call_groq_json(system, prompt, model=HEAVY_MODEL, max_tokens=2000, fixture="pitch_deck")


# ─── Route ────────────────────────────────────────────────────────────────────
@router.post("/generate-content")
def generate_document_content(
    body: GenerateDocRequest,
    user: dict = Depends(with_rate_limit("generation")),
):
    """
    Generates AI content for Business Plan or Pitch Deck.
    Saves the result to Firestore and returns the document ID.
    PDF rendering happens client-side in the browser.
    """
    if user.get("uid") != body.userId:
        raise HTTPException(status_code=403, detail="Forbidden.")

    if body.docType not in ("business_plan", "pitch_deck"):
        raise HTTPException(
            status_code=400,
            detail="docType must be 'business_plan' or 'pitch_deck'",
        )

    # ── Read context ─────────────────────────────────────────────────────────
    ctx = _get_bff_and_profile(body.userId, body.businessId)
    if not ctx:
        raise HTTPException(status_code=404, detail="Business profile not found.")

    # ── BFF Philosophy context from Pinecone ──────────────────────────────────
    t = business_type.describe(ctx.get("section1", {}))
    query = f"business plan entrepreneurship Nigeria {t['subsector']} {t['niche']}"
    book_context = query_book_context(query, top_k=3)

    # ── Generate ──────────────────────────────────────────────────────────────
    try:
        if body.docType == "business_plan":
            content = _generate_business_plan(ctx, book_context)
            doc_name = f"{ctx.get('businessIdentity', {}).get('businessName', 'Business')}_Plan"
        else:
            content = _generate_pitch_deck(ctx, book_context)
            doc_name = f"{ctx.get('businessIdentity', {}).get('businessName', 'Business')}_PitchDeck"
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")

    # ── Save to Firestore ─────────────────────────────────────────────────────
    from google.cloud.firestore_v1 import SERVER_TIMESTAMP
    db  = firestore.client()
    biz = (
        db.collection("user_data")
        .document(body.userId)
        .collection("businesses")
        .document(body.businessId)
    )
    doc_ref = biz.collection("documents").add({
        "name":         doc_name,
        "type":         body.docType,
        "dataSnapshot": content,
        "createdAt":    SERVER_TIMESTAMP,
        "status":       "ready",
    })
    doc_id = doc_ref[1].id

    return {
        "success":      True,
        "docId":        doc_id,
        "dataSnapshot": content,
    }