← back to Goodquestion

src/app/api/opportunities/route.ts

148 lines

import { NextRequest, NextResponse } from 'next/server'
import { aiProvider } from '@/lib/ai-provider-multi'
import { z } from 'zod'
import JSON5 from 'json5'

const OpportunitiesRequestSchema = z.object({
  location: z.string().min(1),
  capitalOnHand: z.number().optional(),
})

interface BusinessOpportunity {
  id: string
  businessType: string
  description: string
  viabilityScore: number
  reasoning: string
  estimatedStartupCost: number
  estimatedMonthlyRevenue: number
  keyStrengths: string[]
  demandLevel: 'high' | 'medium' | 'low'
  competitionLevel: 'high' | 'medium' | 'low'
  timeToProfit: string
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    const validatedData = OpportunitiesRequestSchema.parse(body)

    const prompt = `You are a business analyst with deep knowledge of ${validatedData.location}'s market, demographics, and economic conditions.

CRITICAL: Use REAL market data and realistic financial projections based on ${validatedData.location}'s:
- Actual median household income
- Real estate costs and commercial rent rates
- Local consumer spending patterns
- Current market saturation levels
- Existing competition analysis
- Actual wage rates for employees
- Real utility and operational costs

${validatedData.capitalOnHand ? `CAPITAL CONSTRAINT: The entrepreneur has ONLY $${validatedData.capitalOnHand.toLocaleString()} available. ALL suggestions must be REALISTICALLY achievable with this budget.` : 'Include businesses across different capital requirements from $5K to $500K.'}

Provide TOP 5 VIABLE business opportunities with BRUTALLY HONEST assessments:

REQUIREMENTS:
1. Use REAL startup costs (equipment, inventory, deposits, permits, insurance, initial marketing)
2. Use REALISTIC monthly revenue (based on actual local market capacity, not wishful thinking)
3. Account for REAL operating expenses (rent, utilities, payroll, insurance, supplies)
4. Consider ACTUAL competition levels in ${validatedData.location}
5. Factor in LOCAL demand and market saturation
6. Include realistic time to profitability (most businesses take 12-24 months)
7. Viability scores should reflect REAL success rates (most businesses fail, be honest)

Return ONLY a JSON object:
{
  "opportunities": [
    {
      "businessType": "Mobile Car Detailing Service",
      "description": "High-end mobile detailing for luxury car owners",
      "viabilityScore": 85,
      "reasoning": "Low barriers to entry, proven demand from 47% luxury vehicle ownership rate, minimal competition in suburbs",
      "estimatedStartupCost": 12000,
      "estimatedMonthlyRevenue": 8500,
      "keyStrengths": ["Low overhead", "Flexible scheduling", "Recurring customers"],
      "demandLevel": "high",
      "competitionLevel": "low",
      "timeToProfit": "4-6 months"
    }
  ]
}

CRITICAL RULES:
- Viability scores: 70-95 (be realistic, few businesses are 95%+ viable)
- Startup costs: Based on ACTUAL market prices in ${validatedData.location}
- Monthly revenue: Conservative estimates based on LOCAL market capacity
- Time to profit: Realistic (most take 6-18 months, some 2+ years)
- Competition: Based on actual market conditions
- NO overly optimistic projections
- Sort by viability score (highest first)
- Return ONLY the JSON, no markdown`

    const response = await aiProvider.generateCompletion(prompt, {
      maxTokens: 3000,
      temperature: 0.7,
    })

    console.log(`[Opportunities] Generated using ${response.provider}${response.model ? ` (${response.model})` : ''} (${response.tokensUsed} tokens)`)
    console.log(`[Opportunities] Response length: ${response.content.length} chars`)

    // Extract and parse JSON
    const jsonMatch = response.content.match(/\{[\s\S]*\}/)
    if (!jsonMatch) {
      console.error('[Opportunities] No JSON found in response:', response.content.substring(0, 500))
      throw new Error('No valid JSON found in response')
    }

    let parsed
    try {
      parsed = JSON5.parse(jsonMatch[0])
    } catch (parseError) {
      console.error('[Opportunities] JSON parse failed. Response:', jsonMatch[0].substring(0, 1000))
      throw new Error('Failed to parse AI response as JSON')
    }

    // Add IDs to opportunities
    const opportunities: BusinessOpportunity[] = (parsed.opportunities || []).map((opp: any, index: number) => ({
      ...opp,
      id: `opp-${Date.now()}-${index}`,
    }))

    // Ensure we have at least some opportunities
    if (opportunities.length === 0) {
      throw new Error('No opportunities generated')
    }

    return NextResponse.json({
      success: true,
      data: {
        location: validatedData.location,
        opportunities,
        generatedAt: new Date().toISOString(),
      },
    })
  } catch (error) {
    console.error('Opportunities generation error:', error)

    if (error instanceof z.ZodError) {
      return NextResponse.json(
        {
          success: false,
          error: 'Invalid request data',
          details: error.errors,
        },
        { status: 400 }
      )
    }

    return NextResponse.json(
      {
        success: false,
        error: 'Failed to generate opportunities',
        message: error instanceof Error ? error.message : 'Unknown error',
      },
      { status: 500 }
    )
  }
}