import { NextRequest } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/lib/auth' import { prisma } from '@/lib/prisma' const SYSTEM_PROMPT = `Você é um advogado sênior brasileiro com mais de 30 anos de experiência em todas as áreas do direito. Você domina completamente a legislação brasileira, incluindo mas não limitado a: - Constituição Federal de 1988 (CF/88) - Código de Processo Civil (CPC - Lei nº 13.105/2015) - Código Civil (CC - Lei nº 10.406/2002) - Código Penal (CP - Decreto-Lei nº 2.848/1940) - Código de Processo Penal (CPP - Decreto-Lei nº 3.689/1941) - Consolidação das Leis do Trabalho (CLT) - Código de Defesa do Consumidor (CDC - Lei nº 8.078/1990) - Código Tributário Nacional (CTN - Lei nº 5.172/1966) - Estatuto da Criança e do Adolescente (ECA) - Lei de Execução Penal (LEP) - Lei Maria da Penha (Lei nº 11.340/2006) - Lei de Improbidade Administrativa (Lei nº 8.429/1992) INSTRUÇÕES RIGOROSAS: 1. ESTRUTURA: Toda peça deve seguir a estrutura formal brasileira: - Endereçamento ao juízo competente (em caixa alta) - Qualificação completa das partes - DOS FATOS (narrativa clara e cronológica) - DO DIREITO / DOS FUNDAMENTOS JURÍDICOS (argumentação robusta) - DOS PEDIDOS (numerados e específicos) - Requerimentos finais - Valor da causa (quando aplicável) - Local, data e assinatura 2. CITAÇÕES LEGAIS: Cite artigos específicos de leis reais. Use jurisprudência quando relevante (STF, STJ, TJs). Formate citações em itálico. 3. LINGUAGEM: Use português jurídico formal. Trate o juiz como "Excelentíssimo(a) Senhor(a) Doutor(a) Juiz(a)" ou "Meritíssimo". Use "data venia", "ad argumentandum tantum", e demais expressões latinas quando apropriado. 4. QUALIDADE: A peça deve ser completa, profissional e pronta para protocolar. Não deixe campos em branco - use [COMPLETAR] apenas para dados específicos do cliente que não foram fornecidos. 5. FORMATAÇÃO: Use parágrafos numerados, seções em negrito/caixa alta, e espaçamento adequado para leitura jurídica. 6. Nunca mencione que é uma IA. Escreva como um advogado real escreveria.` function buildUserPrompt(type: string, area: string, details: Record): string { const typeNames: Record = { 'peticao-inicial': 'Petição Inicial', 'contestacao': 'Contestação', 'apelacao-civel': 'Apelação Cível', 'apelacao-criminal': 'Apelação Criminal', 'recurso': 'Recurso', 'contrato': 'Contrato', 'parecer': 'Parecer Jurídico', 'impugnacao': 'Impugnação', 'habeas-corpus': 'Habeas Corpus', 'mandado-seguranca': 'Mandado de Segurança', 'embargo': 'Embargo', 'recurso-especial': 'Recurso Especial', 'agravo': 'Agravo', 'outros': 'Peça Jurídica', } const areaNames: Record = { 'civil': 'Direito Civil', 'trabalhista': 'Direito Trabalhista', 'penal': 'Direito Penal', 'tributario': 'Direito Tributário', 'familia': 'Direito de Família', 'empresarial': 'Direito Empresarial', 'consumidor': 'Direito do Consumidor', 'administrativo': 'Direito Administrativo', } const typeName = typeNames[type] || type const areaName = areaNames[area] || area let prompt = `Elabore uma ${typeName} completa na área de ${areaName}.\n\n` if (details.autor) prompt += `AUTOR/REQUERENTE: ${details.autor}\n` if (details.reu) prompt += `RÉU/REQUERIDO: ${details.reu}\n` if (details.fatos) prompt += `\nFATOS:\n${details.fatos}\n` if (details.fundamentos) prompt += `\nFUNDAMENTOS JURÍDICOS RELEVANTES:\n${details.fundamentos}\n` if (details.pedidos) prompt += `\nPEDIDOS PRETENDIDOS:\n${details.pedidos}\n` if (details.contexto) prompt += `\nCONTEXTO ADICIONAL:\n${details.contexto}\n` prompt += `\nElabore a peça completa, profissional e pronta para protocolar.` return prompt } export async function POST(req: NextRequest) { try { const session = await getServerSession(authOptions) if (!session?.user?.id) { return new Response(JSON.stringify({ error: 'Não autorizado' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }) } // Check credits const user = await prisma.user.findUnique({ where: { id: session.user.id }, select: { credits: true }, }) if (!user || user.credits < 1) { return new Response(JSON.stringify({ error: 'Créditos insuficientes' }), { status: 402, headers: { 'Content-Type': 'application/json' }, }) } const body = await req.json() const { type, area, details } = body if (!type || !area || !details) { return new Response(JSON.stringify({ error: 'Dados incompletos' }), { status: 400, headers: { 'Content-Type': 'application/json' }, }) } const userPrompt = buildUserPrompt(type, area, details) // Create document record const document = await prisma.document.create({ data: { userId: session.user.id, type: type.toUpperCase().replace(/-/g, '_'), title: `${type} - ${area}`, prompt: userPrompt, content: '', area: area.toUpperCase(), status: 'GENERATING', }, }) // Deduct credit await prisma.user.update({ where: { id: session.user.id }, data: { credits: { decrement: 1 } }, }) const OPENAI_API_KEY = process.env.OPENAI_API_KEY if (!OPENAI_API_KEY) { // Fallback: generate a mock response for development const encoder = new TextEncoder() const mockContent = generateMockDocument(type, area, details) const stream = new ReadableStream({ async start(controller) { const words = mockContent.split(' ') for (let i = 0; i < words.length; i++) { const chunk = (i === 0 ? '' : ' ') + words[i] controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content: chunk, documentId: document.id })}\n\n`)) await new Promise(r => setTimeout(r, 20)) } // Update document await prisma.document.update({ where: { id: document.id }, data: { content: mockContent, wordCount: mockContent.split(/\s+/).length, status: 'COMPLETED', tokens: mockContent.length, }, }) // Log usage await prisma.usageLog.create({ data: { userId: session.user.id, type: 'DOCUMENT', tokens: mockContent.length, cost: 0, }, }) controller.enqueue(encoder.encode(`data: ${JSON.stringify({ done: true, documentId: document.id })}\n\n`)) controller.close() }, }) return new Response(stream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, }) } // Real OpenAI call with streaming const openaiRes = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${OPENAI_API_KEY}`, }, body: JSON.stringify({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: SYSTEM_PROMPT }, { role: 'user', content: userPrompt }, ], stream: true, temperature: 0.4, max_tokens: 8000, }), }) if (!openaiRes.ok) { await prisma.document.update({ where: { id: document.id }, data: { status: 'ERROR' }, }) // Refund credit await prisma.user.update({ where: { id: session.user.id }, data: { credits: { increment: 1 } }, }) return new Response(JSON.stringify({ error: 'Erro ao gerar documento' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }) } const encoder = new TextEncoder() let fullContent = '' let totalTokens = 0 const transformStream = new TransformStream({ async transform(chunk, controller) { const text = new TextDecoder().decode(chunk) const lines = text.split('\n').filter(line => line.startsWith('data: ')) for (const line of lines) { const data = line.slice(6).trim() if (data === '[DONE]') { // Save final document const wordCount = fullContent.split(/\s+/).filter(Boolean).length await prisma.document.update({ where: { id: document.id }, data: { content: fullContent, wordCount, status: 'COMPLETED', tokens: totalTokens, }, }) await prisma.usageLog.create({ data: { userId: session.user.id, type: 'DOCUMENT', tokens: totalTokens, cost: (totalTokens / 1000) * 0.005, }, }) controller.enqueue(encoder.encode(`data: ${JSON.stringify({ done: true, documentId: document.id })}\n\n`)) return } try { const parsed = JSON.parse(data) const content = parsed.choices?.[0]?.delta?.content if (content) { fullContent += content totalTokens += content.length // Approximate controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content, documentId: document.id })}\n\n`)) } } catch { // Skip malformed chunks } } }, }) const responseStream = openaiRes.body!.pipeThrough(transformStream) return new Response(responseStream, { headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }, }) } catch (error) { console.error('Document generation error:', error) return new Response(JSON.stringify({ error: 'Erro interno do servidor' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }) } } function generateMockDocument(type: string, area: string, details: Record): string { const autor = details.autor || '[NOME DO AUTOR]' const reu = details.reu || '[NOME DO RÉU]' return `EXCELENTÍSSIMO(A) SENHOR(A) DOUTOR(A) JUIZ(A) DE DIREITO DA ___ VARA CÍVEL DA COMARCA DE [CIDADE] - ESTADO DE [UF] ${autor}, brasileiro(a), [estado civil], [profissão], portador(a) do RG nº [COMPLETAR] e CPF nº [COMPLETAR], residente e domiciliado(a) na [endereço completo], por intermédio de seu(sua) advogado(a) que esta subscreve, com escritório profissional na [endereço do escritório], onde recebe intimações, vem, respeitosamente, perante Vossa Excelência, com fundamento nos artigos 319 e seguintes do Código de Processo Civil (Lei nº 13.105/2015), propor a presente PETIÇÃO INICIAL em face de ${reu}, [qualificação completa do réu], pelos fatos e fundamentos a seguir expostos: I - DOS FATOS ${details.fatos || '1. [Narrar os fatos de forma clara e cronológica, indicando datas, locais e circunstâncias relevantes.]'} 2. Conforme restará demonstrado, os fatos narrados configuram clara violação aos direitos do(a) Autor(a), ensejando a tutela jurisdicional ora pleiteada. 3. Diante da situação narrada, não restou alternativa ao(à) Autor(a) senão buscar a tutela do Poder Judiciário para ver seus direitos resguardados. II - DO DIREITO ${details.fundamentos || `4. O presente caso encontra amparo no ordenamento jurídico brasileiro, especialmente nos seguintes dispositivos legais: 5. A Constituição Federal de 1988, em seu artigo 5º, inciso XXXV, assegura que "a lei não excluirá da apreciação do Poder Judiciário lesão ou ameaça a direito". Trata-se do princípio da inafastabilidade da jurisdição, que garante ao(à) Autor(a) o direito de buscar a tutela jurisdicional. 6. O Código Civil (Lei nº 10.406/2002), em seu artigo 186, estabelece que "aquele que, por ação ou omissão voluntária, negligência ou imprudência, violar direito e causar dano a outrem, ainda que exclusivamente moral, comete ato ilícito". 7. Nesse sentido, o artigo 927 do mesmo diploma legal determina que "aquele que, por ato ilícito (arts. 186 e 187), causar dano a outrem, fica obrigado a repará-lo". 8. A jurisprudência do Superior Tribunal de Justiça é pacífica nesse sentido: "CIVIL E PROCESSUAL CIVIL. RESPONSABILIDADE CIVIL. DANO MORAL. CONFIGURAÇÃO. O dano moral prescinde de prova quando decorre de fato por si só lesivo à dignidade da pessoa humana." (STJ, AgRg no AREsp nº XXXXX/SP, Rel. Min. XXXXX, DJe XX/XX/XXXX).`} III - DOS PEDIDOS Ante o exposto, requer a Vossa Excelência: ${details.pedidos || `a) A citação do(a) Réu(Ré) para, querendo, apresentar contestação no prazo legal, sob pena de revelia e confissão quanto à matéria de fato; b) A procedência total dos pedidos formulados nesta inicial; c) A condenação do(a) Réu(Ré) ao pagamento de indenização por danos morais, em valor a ser arbitrado por Vossa Excelência, observados os princípios da razoabilidade e proporcionalidade; d) A condenação do(a) Réu(Ré) ao pagamento das custas processuais e honorários advocatícios, nos termos do artigo 85 do CPC;`} e) A produção de todas as provas admitidas em direito, especialmente documental, testemunhal, pericial e depoimento pessoal do(a) Réu(Ré), sob pena de confesso(a). Dá-se à causa o valor de R$ [COMPLETAR] ([valor por extenso]). Termos em que, Pede deferimento. [Cidade], [data]. _______________________________ [NOME DO ADVOGADO] OAB/[UF] nº [COMPLETAR]` }