import { NextRequest, NextResponse } from 'next/server'; import { sendBriefToClient, sendLeadNotification } from '@/lib/email'; // ─── Types ──────────────────────────────────────────────────────────────────── interface ConfigureRequestBody { services: string[]; aiEnabled: boolean; aiType: string | null; industry: string | null; scope: string; timeline: string | null; name: string; company: string; email: string; } // ─── Formatting helpers ────────────────────────────────────────────────────── const SERVICE_NAMES: Record = { web: 'Web Design & Development', systems: 'Custom Software', infrastructure: 'Private Infrastructure', }; const INDUSTRY_NAMES: Record = { maritime: 'Maritime & Yachting', hospitality: 'Hospitality', technology: 'Technology', realestate: 'Real Estate', finance: 'Finance', ngo: 'NGO & Nonprofit', other: 'Other', }; const TIMELINE_NAMES: Record = { asap: 'As soon as possible', '1-3months': '1–3 months', '3-6months': '3–6 months', exploring: 'Just exploring', }; const AI_TYPE_NAMES: Record = { teammate: 'Internal AI Teammate', 'customer-facing': 'Customer-Facing AI', 'data-intelligence': 'Data Intelligence', notsure: 'AI Integration (approach TBD)', }; function buildContext(body: ConfigureRequestBody): string { const services = body.services.map((s) => SERVICE_NAMES[s] ?? s).join(', '); const industry = body.industry ? INDUSTRY_NAMES[body.industry] ?? body.industry : 'Not specified'; const timeline = body.timeline ? TIMELINE_NAMES[body.timeline] ?? body.timeline : 'Not specified'; const company = body.company.trim() || 'Not specified'; const aiType = body.aiEnabled && body.aiType ? AI_TYPE_NAMES[body.aiType] ?? body.aiType : null; let context = `Client Name: ${body.name} Company: ${company} Services Requested: ${services} Industry: ${industry} Timeline: ${timeline}`; if (body.aiEnabled) { context += `\nAI Integration: Yes — ${aiType ?? 'type to be determined'}`; } if (body.scope.trim()) { context += `\nClient's Goals: "${body.scope.trim()}"`; } return context; } // ─── AI Brief Generation ───────────────────────────────────────────────────── async function generateBriefWithAI(body: ConfigureRequestBody): Promise { const apiKey = process.env.OPENROUTER_API_KEY; if (!apiKey) { return generateFallbackBrief(body); } const context = buildContext(body); const displayName = body.name.split(' ')[0] || body.name; const systemPrompt = `You are writing a project brief on behalf of LetsBe Solutions, a digital studio that builds custom websites, custom software, and private digital infrastructure. The company is American-founded and serves businesses on the Côte d'Azur and internationally. Key facts about LetsBe: - Every project is designed and coded from scratch — no templates, no page builders - They build custom software (CRMs, management platforms, association systems, etc.) - They deploy private infrastructure on dedicated servers that the client fully owns and controls - They can layer AI integration into any system they build - Small, experienced team with decades of combined experience in design and engineering - They emphasize data ownership, privacy, and digital sovereignty Write in a professional but warm tone. Be specific and practical — no empty buzzwords. The brief should feel like it was written by someone who understood the client's needs, not a generic template.`; const userPrompt = `Generate a personalized project brief for the following prospect. The brief should: 1. Address the client by first name (${displayName}) 2. Acknowledge their specific industry and goals 3. For each service they selected, describe concretely what LetsBe would build and why it matters for their business 4. If AI integration is requested, explain practically what that would look like 5. Propose a clear engagement approach (discovery → strategy → build → launch) 6. Include a timeline note based on their preference 7. End with clear next steps Format the brief using **bold** for section headings and --- for separators. Keep it concise but substantive — around 400-600 words. Client details: ${context}`; try { const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}`, 'HTTP-Referer': process.env.NEXT_PUBLIC_SITE_URL || 'https://letsbe.biz', 'X-Title': 'LetsBe Project Configurator', }, body: JSON.stringify({ model: 'deepseek/deepseek-v3.2', messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: userPrompt }, ], max_tokens: 1500, temperature: 0.7, }), }); if (!response.ok) { console.error('OpenRouter API error:', response.status, response.statusText); return generateFallbackBrief(body); } const data = await response.json(); const content = data.choices?.[0]?.message?.content; if (!content) { return generateFallbackBrief(body); } return content; } catch (error) { console.error('AI brief generation failed:', error); return generateFallbackBrief(body); } } // ─── Fallback Brief (no API key or API failure) ────────────────────────────── function generateFallbackBrief(body: ConfigureRequestBody): string { const { services, aiEnabled, aiType, industry, scope, timeline, name, company } = body; const serviceNames = services.map((s) => SERVICE_NAMES[s] ?? s); const servicesList = serviceNames.length <= 2 ? serviceNames.join(' and ') : `${serviceNames.slice(0, -1).join(', ')}, and ${serviceNames[serviceNames.length - 1]}`; const industryLabel = industry ? INDUSTRY_NAMES[industry] ?? industry : 'your industry'; const displayCompany = company.trim() || 'your organization'; const displayName = name.split(' ')[0] || 'there'; const timelineStr = timeline ? TIMELINE_NAMES[timeline]?.toLowerCase() ?? 'a timeline to be agreed upon' : 'a timeline to be agreed upon'; const hasWeb = services.includes('web'); const hasSystems = services.includes('systems'); const hasInfra = services.includes('infrastructure'); let sections = ''; if (hasWeb) { sections += `\n**Web Design & Development**\nWe'll design and build a custom website for ${displayCompany} from scratch — no templates, no page builders. Modern, responsive, fast, and optimized for search engines from day one.\n`; } if (hasSystems) { sections += `\n**Custom Software**\nWe'll build a purpose-made system tailored to how ${displayCompany} actually operates — custom data model, role-based access, and integrations with your existing tools.\n`; } if (hasInfra) { sections += `\n**Private Infrastructure**\nWe'll set up a dedicated server environment for ${displayCompany} with email, cloud storage, and business tools that you fully own and control.\n`; } if (aiEnabled && aiType) { const aiLabel = AI_TYPE_NAMES[aiType] ?? 'AI integration'; sections += `\n**AI Integration**\nWe'll layer ${aiLabel.toLowerCase()} into your systems — deeply integrated, not bolted on. The exact approach will be scoped during discovery.\n`; } if (scope?.trim()) { sections += `\n**Your Goals**\nYou shared: "${scope.trim()}" — we'll frame our discovery sessions around these priorities.\n`; } return `**Project Brief for ${displayCompany}** Prepared for: ${name} Date: ${new Date().toLocaleDateString('en-GB', { year: 'numeric', month: 'long', day: 'numeric' })} --- **Overview** Hi ${displayName}, based on your interest in ${servicesList} for the ${industryLabel} sector, here's a preliminary brief to guide our first conversation. We'll approach this as a unified project — every component working together, fully owned and controlled by you. ${sections} **Our Approach** We start with a Discovery phase (2–3 sessions) to understand your requirements before writing any code. This ensures we build exactly what you need. **Timeline** Target delivery: ${timelineStr}. A detailed roadmap will follow the Discovery phase. **Next Steps** 1. Book a 30-minute introductory call 2. We'll share a detailed scope document within 48 hours 3. Discovery begins — no obligation Looking forward to building something great together. — The LetsBe Team`; } // ─── Route Handler ──────────────────────────────────────────────────────────── export async function POST(request: NextRequest) { try { const body = (await request.json()) as ConfigureRequestBody; // Validate required fields if (!body.services || body.services.length === 0) { return NextResponse.json( { success: false, error: 'At least one service must be selected.' }, { status: 400 }, ); } if (!body.name || body.name.trim().length < 2) { return NextResponse.json( { success: false, error: 'A valid name is required.' }, { status: 400 }, ); } if (!body.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(body.email)) { return NextResponse.json( { success: false, error: 'A valid email address is required.' }, { status: 400 }, ); } // Generate the brief (AI if available, fallback otherwise) const brief = await generateBriefWithAI(body); // Send emails (non-blocking — don't fail the response if email fails) if (process.env.SMTP_HOST && process.env.SMTP_PASS) { Promise.allSettled([ sendBriefToClient({ to: body.email, name: body.name, company: body.company, brief, }), sendLeadNotification({ to: process.env.ADMIN_EMAIL || 'hello@letsbe.biz', name: body.name, company: body.company, brief, services: body.services, email: body.email, }), ]).catch(() => { console.error('Email sending failed'); }); } return NextResponse.json({ success: true, brief }); } catch { return NextResponse.json( { success: false, error: 'An unexpected error occurred. Please try again.' }, { status: 500 }, ); } }