import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' import OpenAI from 'https://esm.sh/openai@4.20.1' const openai = new OpenAI({ apiKey: Deno.env.get('OPENAI_API_KEY') }); interface StoryPrompt { theme_id: string; subject_id: string; character_id: string; setting_id: string; context?: string; } const ALLOWED_ORIGINS = [ 'http://localhost:5173', // Vite dev server 'http://localhost:3000', // Caso use outro port 'https://historiasmagicas.netlify.app' // Produção ]; serve(async (req) => { const origin = req.headers.get('origin') || ''; const corsHeaders = { 'Access-Control-Allow-Origin': ALLOWED_ORIGINS.includes(origin) ? origin : ALLOWED_ORIGINS[0], 'Access-Control-Allow-Methods': 'POST, OPTIONS', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', 'Access-Control-Max-Age': '86400', // 24 horas }; // Preflight request if (req.method === 'OPTIONS') { return new Response('ok', { headers: corsHeaders }) } const { record } = await req.json() console.log('[Request]', record) try { const supabase = createClient( Deno.env.get('SUPABASE_URL') ?? '', Deno.env.get('SUPABASE_ANON_KEY') ?? '' ) console.log('[Supabase] Cliente inicializado') console.log('[DB] Buscando categorias...') const [themeResult, subjectResult, characterResult, settingResult] = await Promise.all([ supabase.from('story_themes').select('*').eq('id', record.theme_id).single(), supabase.from('story_subjects').select('*').eq('id', record.subject_id).single(), supabase.from('story_characters').select('*').eq('id', record.character_id).single(), supabase.from('story_settings').select('*').eq('id', record.setting_id).single() ]) console.log('[DB] Resultados das consultas:', { theme: themeResult, subject: subjectResult, character: characterResult, setting: settingResult }) if (themeResult.error) throw new Error(`Erro ao buscar tema: ${themeResult.error.message}`); if (subjectResult.error) throw new Error(`Erro ao buscar disciplina: ${subjectResult.error.message}`); if (characterResult.error) throw new Error(`Erro ao buscar personagem: ${characterResult.error.message}`); if (settingResult.error) throw new Error(`Erro ao buscar cenário: ${settingResult.error.message}`); if (!themeResult.data) throw new Error(`Tema não encontrado: ${record.theme_id}`); if (!subjectResult.data) throw new Error(`Disciplina não encontrada: ${record.subject_id}`); if (!characterResult.data) throw new Error(`Personagem não encontrado: ${record.character_id}`); if (!settingResult.data) throw new Error(`Cenário não encontrado: ${record.setting_id}`); const theme = themeResult.data; const subject = subjectResult.data; const character = characterResult.data; const setting = settingResult.data; console.log('[Validation] Categorias validadas com sucesso') console.log('[GPT] Construindo prompt...') const prompt = ` Crie uma história educativa para crianças com as seguintes características: Tema: ${theme.title} Disciplina: ${subject.title} Personagem Principal: ${character.title} Cenário: ${setting.title} ${record.context ? `Contexto Adicional: ${record.context}` : ''} Requisitos: - História adequada para crianças de 6-12 anos - Conteúdo educativo focado em ${subject.title} - Linguagem clara e envolvente - 3-5 páginas de conteúdo - Cada página deve ter um texto curto e sugestão para uma imagem - Evitar conteúdo sensível ou inadequado - Incluir elementos de ${theme.title} - Ambientado em ${setting.title} - Personagem principal baseado em ${character.title} Formato da resposta: { "title": "Título da História", "pages": [ { "text": "Texto da página", "image_prompt": "Descrição para gerar a imagem" } ] } ` console.log('[GPT] Prompt construído:', prompt) console.log('[GPT] Iniciando geração da história...') const completion = await openai.chat.completions.create({ model: "gpt-4o-mini", messages: [ { role: "system", content: "Você é um contador de histórias infantis especializado em conteúdo educativo." }, { role: "user", content: prompt } ], temperature: 0.7, max_tokens: 1000 }) console.log('[GPT] História gerada:', completion.choices[0].message) const storyContent = JSON.parse(completion.choices[0].message.content || '{}') // Validar estrutura do retorno da IA if (!storyContent.title || !Array.isArray(storyContent.pages)) { throw new Error('Formato inválido retornado pela IA'); } console.log('[DALL-E] Iniciando geração de imagens...') const pages = await Promise.all( storyContent.pages.map(async (page: any, index: number) => { console.log(`[DALL-E] Gerando imagem ${index + 1}/${storyContent.pages.length}...`) const imageResponse = await openai.images.generate({ prompt: `${page.image_prompt}. Style: children's book illustration, colorful, educational, safe for kids`, n: 1, size: "1024x1024" }) console.log(`[DALL-E] Imagem ${index + 1} gerada com sucesso`) return { text: page.text, image: imageResponse.data[0].url } }) ) console.log('[DALL-E] Todas as imagens geradas com sucesso') // Preparar dados para salvar const storyData = { title: storyContent.title, content: { title: storyContent.title, pages: pages, theme: theme.title, subject: subject.title, character: character.title, setting: setting.title, context: record.context, original_prompt: prompt, ai_response: completion.choices[0].message.content }, status: 'published', theme_id: theme.id, subject_id: subject.id, character_id: character.id, setting_id: setting.id, updated_at: new Date().toISOString() } console.log('[DB] Dados para salvar:', storyData) // Atualizar história no Supabase console.log('[DB] Salvando história...') const { data: savedStory, error: updateError } = await supabase .from('stories') .update(storyData) .eq('id', record.id) .select() .single() if (updateError) { throw new Error(`Erro ao salvar história: ${updateError.message}`); } console.log('[DB] História salva com sucesso:', savedStory) return new Response( JSON.stringify({ success: true, message: 'História gerada e salva com sucesso', storyId: record.id, story: savedStory }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ) } catch (error) { console.error('[Error] Erro ao gerar história:', error) console.error('[Error] Stack trace:', error.stack) return new Response( JSON.stringify({ error: error.message, stack: error.stack, timestamp: new Date().toISOString() }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 500 } ) } })