require('dotenv').config();
const express = require('express');
const cookieParser = require('cookie-parser');
const path = require('path');
const multer = require('multer');

const app = express();
const PORT = process.env.PORT || 3000;
const ACCESS_PASSWORD = process.env.ACCESS_PASSWORD || 'puglindo26';

// Configuração do multer para uploads de áudio
const multerUpload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 25 * 1024 * 1024 } // 25MB max
});

app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(cookieParser(process.env.SESSION_SECRET || 'camila-secret'));

// Middleware para verificar autenticação
const requireAuth = (req, res, next) => {
  const session = req.signedCookies.camila_session || req.cookies.camila_session;
  if (session === 'authenticated') {
    next();
  } else {
    if (req.xhr || req.path.startsWith('/api/')) {
      res.status(401).json({ error: 'Não autorizado' });
    } else {
      res.redirect('/login');
    }
  }
};

const fs = require('fs');
const { Pool } = require('pg');
const dbPool = new Pool({
  connectionString: process.env.DATABASE_URL
});

const CONFIG_FILE_PATH = path.join(__dirname, 'agent_config.json');
const KNOWLEDGE_FILE_PATH = path.join(__dirname, 'conhecimento_adquirido.json');
const TEMPERATURE_PRESETS = {
  tecnico: { value: 0.2, label: 'Técnico e Direto', description: 'Respostas curtas, objetivas, sem rodeios. Ideal para quando precisa só do essencial.' },
  equilibrado: { value: 0.6, label: 'Equilibrado', description: 'Bom para uso geral — nem muito curto, nem muito longo.' },
  detalhista: { value: 1.0, label: 'Detalhista e Falante', description: 'Respostas extensas, explicações completas,contextualização máxima. Ideal para estudo e revisão.' }
};

let agentConfigCache = {
  agentName: "Kemily",
  kemilyAvatarUrl: "assets/kemily.png",
  camilaAvatarUrl: "assets/camila_prof.png",
  temperaturePreset: "equilibrado"
};

function readAgentConfig() {
  return agentConfigCache;
}

async function writeAgentConfig(config) {
  agentConfigCache = {
    agentName: config.agentName,
    kemilyAvatarUrl: config.kemilyAvatarUrl,
    camilaAvatarUrl: config.camilaAvatarUrl,
    temperaturePreset: config.temperaturePreset || 'equilibrado'
  };
  try {
    await dbPool.query(
      `INSERT INTO escola.config (key, agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset, updated_at)
       VALUES ('current', $1, $2, $3, $4, NOW())
       ON CONFLICT (key) DO UPDATE SET
         agent_name = EXCLUDED.agent_name,
         kemily_avatar_url = EXCLUDED.kemily_avatar_url,
         camila_avatar_url = EXCLUDED.camila_avatar_url,
         temperature_preset = EXCLUDED.temperature_preset,
         updated_at = NOW();`,
      [config.agentName, config.kemilyAvatarUrl, config.camilaAvatarUrl, config.temperaturePreset || 'equilibrado']
    );
    fs.writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2), 'utf8');
    const botConfigDir = path.join(__dirname, '..', 'BotVPS', 'data');
    if (fs.existsSync(botConfigDir)) {
      fs.writeFileSync(path.join(botConfigDir, 'agent_config.json'), JSON.stringify(config, null, 2), 'utf8');
    }
  } catch (e) {
    console.error('Erro ao salvar agent_config no banco:', e);
  }
}

// Leitura/escrita do conhecimento adquirido
function readConhecimento() {
  // Mantido apenas para compatibilidade de migração na inicialização
  try {
    if (fs.existsSync(KNOWLEDGE_FILE_PATH)) {
      return JSON.parse(fs.readFileSync(KNOWLEDGE_FILE_PATH, 'utf8'));
    }
  } catch (e) {
    console.error('Erro ao ler conhecimento_adquirido.json:', e);
  }
  return { facts: [], lastUpdated: null };
}

async function addKnowledgeFact(fact, source = 'conversa') {
  try {
    const res = await dbPool.query("SELECT content FROM escola.conhecimento;");
    const exists = res.rows.some(f => 
      f.content.toLowerCase().includes(fact.toLowerCase()) || 
      fact.toLowerCase().includes(f.content.toLowerCase())
    );
    if (!exists) {
      await dbPool.query(
        "INSERT INTO escola.conhecimento (content, source, added_at) VALUES ($1, $2, NOW());",
        [fact, source]
      );
    }
    return !exists;
  } catch (err) {
    console.error('Erro ao adicionar fato no banco:', err);
    return false;
  }
}

async function removeKnowledgeFact(id) {
  try {
    await dbPool.query("DELETE FROM escola.conhecimento WHERE id = $1;", [id]);
  } catch (err) {
    console.error('Erro ao remover fato do banco:', err);
  }
}

async function getKnowledgeAsText() {
  try {
    const res = await dbPool.query("SELECT content, source FROM escola.conhecimento ORDER BY added_at DESC;");
    if (!res.rows.length) return '';
    return '\n\n════════════════════════════════════════\nCONHECIMENTO ADQUIRIDO (Aprendido com a Camila):\n════════════════════════════════════════\n' + 
      res.rows.map(f => `[${f.source}] ${f.content}`).join('\n') +
      '\n════════════════════════════════════════\n';
  } catch (err) {
    console.error('Erro ao ler conhecimento do banco:', err);
    return '';
  }
}

// Rota GET para obter configurações do agente
app.get('/api/agent-config', requireAuth, (req, res) => {
  const config = readAgentConfig();
  res.json(config);
});

// Rota POST para atualizar configurações do agente
app.post('/api/agent-config', requireAuth, async (req, res) => {
  const { agentName, kemilyAvatarUrl, camilaAvatarUrl, temperaturePreset } = req.body;
  if (!agentName) {
    return res.status(400).json({ error: 'Nome do agente é obrigatório' });
  }
  const config = {
    agentName: agentName.trim(),
    kemilyAvatarUrl: kemilyAvatarUrl || "assets/kemily.png",
    camilaAvatarUrl: camilaAvatarUrl || "assets/camila_prof.png",
    temperaturePreset: temperaturePreset || 'equilibrado'
  };
  await writeAgentConfig(config);
  res.json({ success: true, config });
});

// Rotas de conhecimento adquirido
app.get('/api/conhecimento', requireAuth, async (req, res) => {
  try {
    const dbRes = await dbPool.query("SELECT id, content, source, added_at as \"addedAt\" FROM escola.conhecimento ORDER BY added_at DESC;");
    res.json({ facts: dbRes.rows });
  } catch (err) {
    console.error('Erro ao listar conhecimento:', err);
    res.status(500).json({ error: err.message });
  }
});

app.post('/api/conhecimento', requireAuth, async (req, res) => {
  const { fact, source } = req.body;
  if (!fact) return res.status(400).json({ error: 'Fato é obrigatório' });
  const added = await addKnowledgeFact(fact, source || 'conversa');
  res.json({ success: true, added });
});

app.delete('/api/conhecimento/:id', requireAuth, async (req, res) => {
  await removeKnowledgeFact(parseInt(req.params.id));
  res.json({ success: true });
});

app.put('/api/conhecimento/:id', requireAuth, async (req, res) => {
  const { content } = req.body;
  if (!content) return res.status(400).json({ error: 'Conteúdo é obrigatório' });
  try {
    const id = parseInt(req.params.id);
    await dbPool.query("UPDATE escola.conhecimento SET content = $1 WHERE id = $2;", [content, id]);
    res.json({ success: true });
  } catch (err) {
    console.error('Erro ao atualizar conhecimento:', err);
    res.status(500).json({ error: err.message });
  }
});

// Analisar mensagem final e extrair conhecimento aprendido (chamado pelo frontend)
app.post('/api/conhecimento/extrair', requireAuth, async (req, res) => {
  const { message } = req.body;
  if (!message) return res.status(400).json({ error: 'Mensagem é obrigatória' });
  
  const knowledgeRegex = /\[KNOWLEDGE:\s*([^\]]+)\]/gi;
  const facts = [];
  let match;
  while ((match = knowledgeRegex.exec(message)) !== null) {
    const fact = match[1].trim();
    if (fact && fact.length > 3) {
      const added = await addKnowledgeFact(fact, 'conversa');
      if (added) facts.push(fact);
    }
  }
  
  res.json({ success: true, factsFound: facts.length, facts });
});

app.get('/api/temperature-presets', requireAuth, (req, res) => {
  res.json(TEMPERATURE_PRESETS);
});

// ============================================================
// SISTEMA DE OBSERVAÇÃO POR VOZ
// ============================================================
const OBSERVACOES_DIR = path.join(__dirname, 'observacoes');
const OBS_INDEX_FILE = path.join(OBSERVACOES_DIR, 'index.json');

// Garante que diretório de observações existe
if (!fs.existsSync(OBSERVACOES_DIR)) {
  fs.mkdirSync(OBSERVACOES_DIR, { recursive: true });
}

// Lê índice de observações (mantido apenas para fins de migração inicial)
function readObsIndex() {
  if (!fs.existsSync(OBS_INDEX_FILE)) return { observations: [] };
  try {
    return JSON.parse(fs.readFileSync(OBS_INDEX_FILE, 'utf8'));
  } catch { return { observations: [] }; }
}

function writeObsIndex(idx) {
  try {
    fs.writeFileSync(OBS_INDEX_FILE, JSON.stringify(idx, null, 2), 'utf8');
  } catch (err) {
    console.error('Erro ao escrever index local:', err);
  }
}

// Inicializar banco de dados e migrar dados se estiver vazio
async function initDatabase() {
  try {
    // 0. Garantir que a tabela de modelos exista e tenha dados padrão
    await dbPool.query(`
      CREATE TABLE IF NOT EXISTS escola.modelos (
        id SERIAL PRIMARY KEY,
        nome VARCHAR(255) NOT NULL,
        finalidade TEXT,
        periodicidade VARCHAR(100),
        estrutura TEXT,
        created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
      );
    `);

    const checkModelos = await dbPool.query("SELECT COUNT(*) FROM escola.modelos;");
    if (parseInt(checkModelos.rows[0].count) === 0) {
      console.log('[Database Init] Inserindo modelos/templates padrão de relatório...');
      await dbPool.query(`
        INSERT INTO escola.modelos (nome, finalidade, periodicidade, estrutura) VALUES
        ($1, $2, $3, $4),
        ($5, $6, $7, $8);
      `, [
        "Relatório Semanal de Desenvolvimento",
        "Acompanhar a evolução cognitiva e social das crianças semanalmente",
        "Semanal",
        `# Relatório Semanal de Desenvolvimento Pedagógico\n\n## 1. Aspectos Cognitivos e Linguagem\n- Como a criança interagiu com os materiais e desafios propostos esta semana.\n- Expressão verbal, argumentação e participação em rodas de conversa.\n\n## 2. Aspectos Socioemocionais e Autonomia\n- Colaboração com os colegas e resolução de pequenos conflitos.\n- Nível de independência na realização das atividades diárias.\n\n## 3. Aspectos Motores e Brincar\n- Coordenação motora ampla e fina durante as brincadeiras e atividades direcionadas.`,
        "Avaliação Semestral Individual",
        "Avaliar de forma simples e objetiva o progresso do aluno ao fim do semestre, combinando a introdução global da turma com o relatório individual de desenvolvimento.",
        "Semestral",
        `# Relatório Semestral de Avaliação — [Nome do Aluno]\n\n## Introdução Comum (Observações Globais da Turma)\nEm continuidade ao trabalho iniciado no semestre anterior, o planejamento das ações em relação às crianças visa favorecer o desenvolvimento físico, cognitivo, afetivo e emocional, bem como incentivar a convivência harmoniosa entre os colegas e adultos, valorizar a organização e a conservação do material individual e de uso coletivo, bem como dos espaços, respeitar os combinados para um bom andamento da rotina e para proporcionar um ambiente que seja convidativo e atividades que sejam prazerosas, permitindo que as crianças tenham oportunidade de se desenvolver, de se relacionar e se expressar com liberdade, através dos diversos movimentos propostos pela escola, a fim de ampliar as aprendizagens.\n\nDurante as brincadeiras e interações, as crianças experienciam a todo momento muitas situações que trazem na memória em relação à rotina de suas vidas e as reproduzem, e durante essas vivências simbólicas, são capazes de recriar histórias, compartilhar conflitos e emoções, solicitar auxílio e solidarizar-se com as necessidades do próximo, opinar e argumentar sobre suas impressões e pontos de vista, e a partir das vivências lúdicas, observamos avanços significativos no uso da linguagem e raciocínio lógico, no entendimento de mundo, compreensão e respeito aos valores humanos.\n\nCompreendemos que é direito das crianças o contato com a cultura escrita, a fim de incentivar a curiosidade pelo mundo letrado e despertar o interesse pela leitura. Portanto, proporcionamos diversos momentos em que as crianças acessam variados portadores e gêneros textuais, como livros, cartazes, listas, avisos, bilhetes, convites, pesquisas, receitas, poesias, canções, rótulos, embalagens, revistas, jornais, painéis, entre outros, realizando a leitura desses portadores juntamente com as crianças, para que conheçam a função social da escrita.\n\nNeste período continuamos a desenvolver nossas pesquisas sobre a “Identidade e a Diversidade”, com a finalidade de que as crianças despertem o interesse por conhecer e valorizar o patrimônio brasileiro, respeitando nossa diversidade étnica, cultural, religiosa e social. Conhecemos um pouco sobre as origens da nossa cultura alimentar, advinda dos povos originários, africanos e portugueses inicialmente, e depois recebendo influências dos mais diversos povos e culturas provenientes de diversas partes do mundo, com as imigrações e com a miscigenação, e concluímos que as culturas se misturam e se enriquecem. Realizamos o estudo da canção “Tempero”, com destaque para o texto e a recitação da poesia, contextualizada com os temas estudados. Realizamos também uma contação de história envolvendo a “Lenda da Mandioca”, na qual os alunos representaram os personagens por meio de desenho, recorte e colagem. As crianças realizaram o plantio de sementes de “tomate-cereja” e “salsinha”, e realizaram o cuidado diário observando a rega das mudas. A turma preparou um painel envolvendo o tema “Identidade e Diversidade”, demonstrando as atividades realizadas dentro deste contexto.\n\nForam trabalhados também alguns jogos de tabuleiro, dentre eles o “resta-um” e o “quatro-em-linha”, cujas bandejas foram preparadas pelas crianças, utilizando material reciclável. Foram realizadas várias brincadeiras envolvendo cantigas de roda e parlendas, pular cordas, gincanas e circuitos com materiais diversificados, bem como foram oferecidos diariamente vários tipos de brinquedos, incluindo peças de montagem em materiais diversos, quebra-cabeças, bonecos, materiais não estruturados, entre outros, a fim de estimular a atenção e concentração, a criatividade, a agilidade, organização e respeito às regras, bem com o espírito desportivo e de trabalho coletivo.\n\n---\n\n## Desenvolvimento Individual: [Nome do Aluno]\n[Nome do Aluno] apresentou muitos avanços neste período. [Escreva um texto corrido de 1 ou 2 parágrafos detalhando o desenvolvimento do aluno com base nas observações enviadas, integrando de forma natural e sem subtítulos: autonomia/rotina/alimentação, brincadeiras/preferências no parque/areia, amigos próximos (usando aspas como \"a Mirella\"), desenvolvimento motor (pular corda, triciclos, circuitos), linguagem/expressão/leitura de livros, criatividade/atividades com blocos/lego/desenhos, e eventuais pontos de atenção ou desafios formulados com linguagem atenuada (ex. \"necessitando de orientação\", \"permanece o desafio de\")].\n\nEspero que o/a [Nome do Aluno] continue se interessando bastante pela escola e avançando muito em suas aprendizagens no próximo ano.`
      ]);
    }

    // 1. Sincronizar cache de configurações do agente ou migrar
    const configRes = await dbPool.query("SELECT agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset FROM escola.config WHERE key = 'current';");
    if (configRes.rows.length > 0) {
      agentConfigCache = {
        agentName: configRes.rows[0].agent_name,
        kemilyAvatarUrl: configRes.rows[0].kemily_avatar_url,
        camilaAvatarUrl: configRes.rows[0].camila_avatar_url,
        temperaturePreset: configRes.rows[0].temperature_preset
      };
      console.log('[Database Init] Configurações do agente carregadas do banco de dados:', agentConfigCache.agentName);
    } else {
      console.log('[Database Init] Migrando configurações do agente do arquivo local...');
      let localConfig = {
        agentName: "Kemily",
        kemilyAvatarUrl: "assets/kemily.png",
        camilaAvatarUrl: "assets/camila_prof.png",
        temperaturePreset: "equilibrado"
      };
      try {
        if (fs.existsSync(CONFIG_FILE_PATH)) {
          localConfig = JSON.parse(fs.readFileSync(CONFIG_FILE_PATH, 'utf8'));
        }
      } catch (err) {
        console.error('[Database Init] Erro ao ler agent_config.json local:', err.message);
      }
      agentConfigCache = localConfig;
      await dbPool.query(
        "INSERT INTO escola.config (key, agent_name, kemily_avatar_url, camila_avatar_url, temperature_preset) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (key) DO NOTHING;",
        ['current', localConfig.agentName, localConfig.kemilyAvatarUrl, localConfig.camilaAvatarUrl, localConfig.temperaturePreset || 'equilibrado']
      );
    }

    // 2. Verificar/Migrar fatos de conhecimento
    const knowledgeRes = await dbPool.query("SELECT COUNT(*) FROM escola.conhecimento;");
    if (parseInt(knowledgeRes.rows[0].count) === 0) {
      console.log('[Database Init] Migrando conhecimento do arquivo local...');
      let localKnowledge = { facts: [], lastUpdated: null };
      try {
        if (fs.existsSync(KNOWLEDGE_FILE_PATH)) {
          localKnowledge = JSON.parse(fs.readFileSync(KNOWLEDGE_FILE_PATH, 'utf8'));
        }
      } catch (err) {
        console.error('[Database Init] Erro ao ler conhecimento_adquirido.json local:', err.message);
      }
      for (const fact of localKnowledge.facts || []) {
        await dbPool.query(
          "INSERT INTO escola.conhecimento (content, source, added_at) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING;",
          [fact.content, fact.source, fact.addedAt]
        );
      }
    }

    // 3. Verificar/Migrar observações pedagógicas
    const obsRes = await dbPool.query("SELECT COUNT(*) FROM escola.observacoes;");
    if (parseInt(obsRes.rows[0].count) === 0) {
      console.log('[Database Init] Migrando observações do arquivo local...');
      let localIndex = { observations: [] };
      try {
        if (fs.existsSync(OBS_INDEX_FILE)) {
          localIndex = JSON.parse(fs.readFileSync(OBS_INDEX_FILE, 'utf8'));
        }
      } catch (err) {
        console.error('[Database Init] Erro ao ler index.json local:', err.message);
      }
      for (const obs of localIndex.observations || []) {
        let reportContent = obs.preview;
        try {
          const yearMonth = obs.date.slice(0, 7);
          const filePath = path.join(OBSERVACOES_DIR, yearMonth, obs.filename);
          if (fs.existsSync(filePath)) {
            const rawFile = fs.readFileSync(filePath, 'utf8');
            const parts = rawFile.split('---\n');
            if (parts.length >= 3) {
              reportContent = parts.slice(2).join('---\n').trim();
            } else {
              reportContent = rawFile;
            }
          }
        } catch (fileErr) {
          console.error('[Database Init] Erro ao ler arquivo de relatório:', obs.filename, fileErr.message);
        }

        await dbPool.query(
          "INSERT INTO escola.observacoes (id, filename, date, time, timestamp, criancas, turma, tags, report) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT (id) DO NOTHING;",
          [
            obs.id,
            obs.filename,
            obs.date,
            obs.time,
            obs.timestamp,
            JSON.stringify(obs.criancas),
            obs.turma,
            JSON.stringify(obs.tags),
            reportContent
          ]
        );
      }
    }
    console.log('[Database Init] Inicialização e migração do banco de dados concluídas com sucesso!');
  } catch (err) {
    console.error('[Database Init] Erro crítico ao inicializar banco de dados:', err);
  }
}

// Rota: POST /api/observacao/transcrever — recebe áudio e transcreve via Whisper
app.post('/api/observacao/transcrever', requireAuth, multerUpload.single('audio'), async (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'Nenhum áudio recebido' });
  }

  let ext = 'webm';
  if (req.file.mimetype) {
    if (req.file.mimetype.includes('mp4')) ext = 'mp4';
    else if (req.file.mimetype.includes('ogg')) ext = 'ogg';
    else if (req.file.mimetype.includes('wav')) ext = 'wav';
    else if (req.file.mimetype.includes('mpeg')) ext = 'mp3';
  }

  try {
    let transcribedText = '';
    const fileBlob = new Blob([req.file.buffer], { type: req.file.mimetype || 'audio/webm' });

    if (process.env.OPENAI_API_KEY) {
      console.log('[Smart Transcription] Usando OpenAI Whisper...');
      const whisperFormData = new FormData();
      whisperFormData.append('file', fileBlob, `audio.${ext}`);
      whisperFormData.append('model', 'whisper-1');
      whisperFormData.append('language', 'pt');
      whisperFormData.append('response_format', 'text');

      const whisperRes = await fetch('https://api.openai.com/v1/audio/transcriptions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`
        },
        body: whisperFormData
      });

      if (!whisperRes.ok) {
        const errText = await whisperRes.text();
        console.error('OpenAI Whisper error:', errText);
        throw new Error('Erro na transcrição via OpenAI');
      }
      transcribedText = await whisperRes.text();
    } else if (process.env.GROQ_API_KEY) {
      console.log('[Smart Transcription] Usando Groq Whisper...');
      const whisperFormData = new FormData();
      whisperFormData.append('file', fileBlob, `audio.${ext}`);
      whisperFormData.append('model', 'whisper-large-v3');
      whisperFormData.append('language', 'pt');
      whisperFormData.append('response_format', 'text');

      const whisperRes = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.GROQ_API_KEY}`
        },
        body: whisperFormData
      });

      if (!whisperRes.ok) {
        const errText = await whisperRes.text();
        console.error('Groq Whisper error:', errText);
        throw new Error('Erro na transcrição via Groq');
      }
      transcribedText = await whisperRes.text();
    } else {
      throw new Error('Nenhuma chave de API configurada para transcrição de áudio (OPENAI_API_KEY ou GROQ_API_KEY).');
    }

    if (!transcribedText || transcribedText.trim().length < 2) {
      return res.status(400).json({ error: 'O áudio não contém fala identificável.' });
    }

    res.json({ transcribedText });

  } catch (err) {
    console.error('Erro transcrever:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: POST /api/observacao/analisar — recebe texto transcrito e gera relatório pedagógico
app.post('/api/observacao/analisar', requireAuth, async (req, res) => {
  const { transcribedText } = req.body;
  if (!transcribedText || transcribedText.trim().length < 5) {
    return res.status(400).json({ error: 'Texto muito curto ou inexistente para análise.' });
  }

  try {
    const agentConfig = readAgentConfig();
    const agentName = agentConfig.agentName || "Kemily";

    const reportPrompt = `Você é a ${agentName}, assistente pedagógica da professora Camila Martella Gasparini Reifonas.

Ela acabou de registrar uma observação sobre seus alunos da educação infantil. Sua tarefa é ANALISAR o texto e retornar UM OBJETO JSON (sem markdown, sem código, apenas o JSON puro) com:

{
  "criancas": ["lista de nomes de crianças mencionadas ou 'null' se não identificar nenhuma"],
  "turma": "turma mencionada ou 'não informada'",
  "tags": ["tag1", "tag2"] (social, cognitivo, motor, linguagem, emocional, colaborativo, autónomo),
  "relatorio": "...markdown estruturado do relatório pedagógico..."
}

REGRAS:
- Identifique crianças pelo nome próprio mencionado na fala/texto
- Se não conseguir identificar nomes, use "criancas": null
- Tags: escolha entre social, cognitivo, motor, linguagem, emocional, colaborativo, autónomo
- Relatório em português, linguagem formal-pedagógica, seções claras em **markdown**
- Foque em evidências observáveis
- Máximo 120 palavras no relatório

OBSERVAÇÃO REGISTRADA:
"""
${transcribedText}
"""

Retorne APENAS o JSON válido:`;

    let rawContent = '';

    // Tentar OpenRouter
    try {
      console.log('[Smart Report] Tentando gerar relatório via OpenRouter...');
      const reportRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
          messages: [{ role: 'user', content: reportPrompt }],
          temperature: 0.5,
          max_tokens: 1500
        })
      });

      if (reportRes.ok) {
        const reportData = await reportRes.json();
        rawContent = reportData.choices?.[0]?.message?.content || '';
      } else {
        console.warn('[Smart Report] Falha no OpenRouter:', await reportRes.text());
      }
    } catch (err) {
      console.error('[Smart Report] Erro de conexão com OpenRouter:', err.message);
    }

    // Fallback para Groq se OpenRouter falhar
    if (!rawContent && process.env.GROQ_API_KEY) {
      try {
        console.log('[Smart Report] Usando Groq como fallback para gerar relatório...');
        const reportRes = await fetch('https://api.groq.com/openai/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.GROQ_MODEL || 'llama-3.3-70b-versatile',
            messages: [{ role: 'user', content: reportPrompt }],
            temperature: 0.5,
            max_tokens: 1500
          })
        });

        if (reportRes.ok) {
          const reportData = await reportRes.json();
          rawContent = reportData.choices?.[0]?.message?.content || '';
        } else {
          console.warn('[Smart Report] Falha no Groq:', await reportRes.text());
        }
      } catch (err) {
        console.error('[Smart Report] Erro de conexão com Groq:', err.message);
      }
    }

    // Fallback para Google Gemini se OpenRouter e Groq falharem
    if (!rawContent && process.env.GOOGLE_AI_API_KEY) {
      try {
        console.log('[Smart Report] Usando Google Gemini como fallback para gerar relatório...');
        const reportRes = await fetch('https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.GOOGLE_AI_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash',
            messages: [{ role: 'user', content: reportPrompt }],
            temperature: 0.5,
            max_tokens: 1500
          })
        });

        if (reportRes.ok) {
          const reportData = await reportRes.json();
          rawContent = reportData.choices?.[0]?.message?.content || '';
        } else {
          console.warn('[Smart Report] Falha no Google Gemini:', await reportRes.text());
        }
      } catch (err) {
        console.error('[Smart Report] Erro de conexão com Google Gemini:', err.message);
      }
    }

    // Fallback para MiniMax se OpenRouter, Groq e Google Gemini falharem
    if (!rawContent && process.env.MINIMAX_API_KEY) {
      try {
        console.log('[Smart Report] Usando MiniMax-M2.7 como fallback para gerar relatório...');
        const reportRes = await fetch('https://api.minimaxi.chat/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: 'MiniMax-M2.7',
            messages: [{ role: 'user', content: reportPrompt }],
            temperature: 0.5,
            max_tokens: 1500
          })
        });

        if (reportRes.ok) {
          const reportData = await reportRes.json();
          rawContent = reportData.choices?.[0]?.message?.content || '';
        } else {
          console.warn('[Smart Report] Falha no MiniMax:', await reportRes.text());
        }
      } catch (err) {
        console.error('[Smart Report] Erro de conexão com MiniMax:', err.message);
      }
    }

    if (!rawContent) {
      throw new Error('Falha ao processar o relatório pedagógico em todos os provedores de IA (OpenRouter, Groq, Google Gemini e MiniMax-M2.7).');
    }

    // Parse JSON da resposta
    let parsedData = { criancas: null, turma: 'não informada', tags: [], relatorio: rawContent };
    try {
      const firstBrace = rawContent.indexOf('{');
      const lastBrace = rawContent.lastIndexOf('}');
      if (firstBrace !== -1 && lastBrace !== -1) {
        const jsonString = rawContent.substring(firstBrace, lastBrace + 1);
        const parsed = JSON.parse(jsonString);
        parsedData = {
          criancas: parsed.criancas || null,
          turma: parsed.turma || 'não informada',
          tags: parsed.tags || [],
          relatorio: parsed.relatorio || rawContent
        };
      } else {
        throw new Error('Nenhum JSON encontrado na resposta da IA');
      }
    } catch (parseErr) {
      console.warn('Não foi possível parsear JSON da resposta da IA:', parseErr.message);
      parsedData.relatorio = rawContent;
    }

    res.json(parsedData);

  } catch (err) {
    console.error('Erro ao analisar observação:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: POST /api/observacao/salvar — salva relatório em .md
app.post('/api/observacao/salvar', requireAuth, async (req, res) => {
  const { report, criancas, turma, tags } = req.body;
  if (!report) return res.status(400).json({ error: 'Relatório vazio' });

  const now = new Date();
  const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
  const dateStr = now.toISOString().split('T')[0];
  const timeStr = now.toTimeString().slice(0, 5).replace(':', 'h') + 'min';
  const timestamp = now.toISOString();
  const id = Date.now();
  const filename = `${dateStr}_${timeStr}.md`;

  // Salvar no Banco (Supabase)
  try {
    await dbPool.query(
      "INSERT INTO escola.observacoes (id, filename, date, time, timestamp, criancas, turma, tags, report) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);",
      [id, filename, dateStr, timeStr, timestamp, JSON.stringify(criancas), turma || 'não informada', JSON.stringify(tags), report]
    );
  } catch (dbErr) {
    console.error('Erro ao salvar observação no banco:', dbErr);
  }

  // Monta markdown com frontmatter
  const mdContent = `---\ndata: "${dateStr}"\nhora: "${timeStr}"\ntimestamp: "${timestamp}"\ncriancas: ${JSON.stringify(criancas)}\nturma: "${turma || 'não informada'}"\ntags: ${JSON.stringify(tags)}\n---\n\n${report}\n`;
  const monthDir = path.join(OBSERVACOES_DIR, yearMonth);
  if (!fs.existsSync(monthDir)) fs.mkdirSync(monthDir, { recursive: true });

  const filePath = path.join(monthDir, filename);
  fs.writeFileSync(filePath, mdContent, 'utf8');

  // Também mantemos a escrita no index.json local para fins de compatibilidade
  try {
    const idx = readObsIndex();
    idx.observations.unshift({
      id,
      filename,
      date: dateStr,
      time: timeStr,
      timestamp,
      criancas: criancas || null,
      turma: turma || 'não informada',
      tags: tags || [],
      preview: report.slice(0, 120) + '...'
    });
    writeObsIndex(idx);
  } catch (idxErr) {
    console.error('Erro ao salvar índice local:', idxErr);
  }

  res.json({ success: true, filename, id });
});

// ============================================================
// CRUD DE MODELOS DE RELATÓRIO (TEMPLATES) & COMPILAÇÃO
// ============================================================

// Listar todos os modelos
app.get('/api/modelos', requireAuth, async (req, res) => {
  try {
    const dbRes = await dbPool.query("SELECT * FROM escola.modelos ORDER BY nome ASC;");
    res.json(dbRes.rows);
  } catch (err) {
    console.error('Erro ao buscar modelos:', err);
    res.status(500).json({ error: err.message });
  }
});

// Criar novo modelo
app.post('/api/modelos', requireAuth, async (req, res) => {
  const { nome, finalidade, periodicidade, estrutura } = req.body;
  if (!nome || !estrutura) return res.status(400).json({ error: 'Nome e estrutura são obrigatórios' });
  try {
    const dbRes = await dbPool.query(
      "INSERT INTO escola.modelos (nome, finalidade, periodicidade, estrutura) VALUES ($1, $2, $3, $4) RETURNING *;",
      [nome, finalidade, periodicidade, estrutura]
    );
    res.status(201).json(dbRes.rows[0]);
  } catch (err) {
    console.error('Erro ao criar modelo:', err);
    res.status(500).json({ error: err.message });
  }
});

// Atualizar modelo existente
app.put('/api/modelos/:id', requireAuth, async (req, res) => {
  const { id } = req.params;
  const { nome, finalidade, periodicidade, estrutura } = req.body;
  if (!nome || !estrutura) return res.status(400).json({ error: 'Nome e estrutura são obrigatórios' });
  try {
    const dbRes = await dbPool.query(
      "UPDATE escola.modelos SET nome = $1, finalidade = $2, periodicidade = $3, estrutura = $4 WHERE id = $5 RETURNING *;",
      [nome, finalidade, periodicidade, estrutura, id]
    );
    if (dbRes.rows.length === 0) return res.status(404).json({ error: 'Modelo não encontrado' });
    res.json(dbRes.rows[0]);
  } catch (err) {
    console.error('Erro ao atualizar modelo:', err);
    res.status(500).json({ error: err.message });
  }
});

// Excluir modelo
app.delete('/api/modelos/:id', requireAuth, async (req, res) => {
  const { id } = req.params;
  try {
    const dbRes = await dbPool.query("DELETE FROM escola.modelos WHERE id = $1 RETURNING *;", [id]);
    if (dbRes.rows.length === 0) return res.status(404).json({ error: 'Modelo não encontrado' });
    res.json({ success: true, message: 'Modelo excluído com sucesso' });
  } catch (err) {
    console.error('Erro ao excluir modelo:', err);
    res.status(500).json({ error: err.message });
  }
});

// Obter lista única de crianças já observadas
app.get('/api/observacoes/criancas', requireAuth, async (req, res) => {
  const { ano, turma } = req.query;
  try {
    let sql = `
      SELECT DISTINCT jsonb_array_elements_text(criancas) as nome 
      FROM escola.observacoes 
      WHERE jsonb_typeof(criancas) = 'array'
    `;
    const params = [];
    let pCount = 1;

    if (ano) {
      sql += ` AND substring(date from 1 for 4) = $${pCount}`;
      params.push(ano);
      pCount++;
    }

    if (turma) {
      sql += ` AND turma = $${pCount}`;
      params.push(turma);
      pCount++;
    }

    sql += ` ORDER BY nome;`;
    const dbRes = await dbPool.query(sql, params);
    res.json(dbRes.rows.map(r => r.nome));
  } catch (err) {
    console.error('Erro ao buscar crianças únicas:', err);
    res.status(500).json({ error: err.message });
  }
});

// Obter metadados únicos para filtros (anos letivos e turmas)
app.get('/api/observacoes/metadata', requireAuth, async (req, res) => {
  try {
    const yearsRes = await dbPool.query(`
      SELECT DISTINCT substring(date from 1 for 4) as ano 
      FROM escola.observacoes 
      WHERE date IS NOT NULL AND date != ''
      ORDER BY ano DESC;
    `);
    const turmasRes = await dbPool.query(`
      SELECT DISTINCT turma 
      FROM escola.observacoes 
      WHERE turma IS NOT NULL AND turma != ''
      ORDER BY turma;
    `);
    res.json({
      anos: yearsRes.rows.map(r => r.ano).filter(Boolean),
      turmas: turmasRes.rows.map(r => r.turma).filter(Boolean)
    });
  } catch (err) {
    console.error('Erro ao buscar metadados de observações:', err);
    res.status(500).json({ error: err.message });
  }
});

// Contar observações baseadas em filtros
app.get('/api/observacoes/contar', requireAuth, async (req, res) => {
  const { crianca, dataInicio, dataFim, tags, ano, turma } = req.query;
  try {
    let sql = `SELECT COUNT(*) FROM escola.observacoes WHERE 1=1`;
    const params = [];
    let pCount = 1;

    if (crianca && crianca !== 'todas') {
      sql += ` AND jsonb_typeof(criancas) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(criancas) AS c 
        WHERE LOWER(c) LIKE $${pCount}
      )`;
      params.push(`%${crianca.toLowerCase()}%`);
      pCount++;
    }

    if (dataInicio) {
      sql += ` AND date >= $${pCount}`;
      params.push(dataInicio);
      pCount++;
    }

    if (dataFim) {
      sql += ` AND date <= $${pCount}`;
      params.push(dataFim);
      pCount++;
    }

    if (ano) {
      sql += ` AND substring(date from 1 for 4) = $${pCount}`;
      params.push(ano);
      pCount++;
    }

    if (turma) {
      sql += ` AND turma = $${pCount}`;
      params.push(turma);
      pCount++;
    }

    if (tags) {
      const tagList = tags.split(',').map(t => t.trim().toLowerCase());
      sql += ` AND jsonb_typeof(tags) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(tags) AS t 
        WHERE LOWER(t) = ANY($${pCount})
      )`;
      params.push(tagList);
      pCount++;
    }

    const dbRes = await dbPool.query(sql, params);
    res.json({ count: parseInt(dbRes.rows[0].count) });
  } catch (err) {
    console.error('Erro ao contar observações:', err);
    res.status(500).json({ error: err.message });
  }
});

// Gerar relatório consolidado com IA baseado em observações e um modelo
app.post('/api/modelos/gerar-relatorio', requireAuth, async (req, res) => {
  const { crianca, dataInicio, dataFim, tags, modeloId, ano, turma } = req.body;
  if (!modeloId) return res.status(400).json({ error: 'O ID do modelo é obrigatório' });

  try {
    // 1. Buscar o modelo/template
    const templateRes = await dbPool.query("SELECT * FROM escola.modelos WHERE id = $1;", [modeloId]);
    if (templateRes.rows.length === 0) return res.status(404).json({ error: 'Modelo de relatório não encontrado' });
    const template = templateRes.rows[0];

    // 2. Buscar as observações filtradas
    let sql = `
      SELECT date, time, report, criancas, tags 
      FROM escola.observacoes 
      WHERE 1=1
    `;
    const params = [];
    let pCount = 1;

    if (crianca && crianca !== 'todas') {
      sql += ` AND jsonb_typeof(criancas) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(criancas) AS c 
        WHERE LOWER(c) LIKE $${pCount}
      )`;
      params.push(`%${crianca.toLowerCase()}%`);
      pCount++;
    }

    if (dataInicio) {
      sql += ` AND date >= $${pCount}`;
      params.push(dataInicio);
      pCount++;
    }

    if (dataFim) {
      sql += ` AND date <= $${pCount}`;
      params.push(dataFim);
      pCount++;
    }

    if (ano) {
      sql += ` AND substring(date from 1 for 4) = $${pCount}`;
      params.push(ano);
      pCount++;
    }

    if (turma) {
      sql += ` AND turma = $${pCount}`;
      params.push(turma);
      pCount++;
    }

    if (tags) {
      const tagList = Array.isArray(tags) ? tags : [tags];
      sql += ` AND jsonb_typeof(tags) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(tags) AS t 
        WHERE LOWER(t) = ANY($${pCount})
      )`;
      params.push(tagList.map(t => t.toLowerCase()));
      pCount++;
    }

    sql += ` ORDER BY date ASC`;
    const dbRes = await dbPool.query(sql, params);
    const observations = dbRes.rows;

    if (observations.length === 0) {
      return res.status(400).json({ error: 'Nenhuma observação encontrada para os filtros selecionados.' });
    }

    // 3. Montar prompt para IA usando a estrutura do template
    const combinedObsText = observations.map((o, idx) => {
      const dateText = o.date ? `Data: ${o.date}` : '';
      const tagsText = o.tags ? `Tags: ${Array.isArray(o.tags) ? o.tags.join(', ') : o.tags}` : '';
      return `[Observação #${idx + 1} ${dateText} ${tagsText}]\n${o.report}`;
    }).join('\n\n---\n\n');

    const agentConfig = readAgentConfig();
    const agentName = agentConfig.agentName || "Kemily";

    const isGlobal = crianca === 'todas';
    const systemPrompt = `Você é a ${agentName}, uma assistente pedagógica e especialista em educação infantil.
Seu objetivo é analisar o histórico de observações ${isGlobal ? 'da turma' : 'de uma criança'} e redigir um relatório pedagógico consolidado e profissional ${isGlobal ? 'globalizado da sala de aula / turma' : 'sobre o desenvolvimento individual desta criança'}.

Você DEVE estruturar o relatório seguindo exatamente as seções e diretrizes fornecidas no modelo de relatório selecionado.

MODELO DE RELATÓRIO: "${template.nome}"
FINALIDADE: "${template.finalidade || 'Não informada'}"
PERIODICIDADE: "${template.periodicidade || 'Não informada'}"

ESTRUTURA/DIRETRIZES DO MODELO:
"""
${template.estrutura}
"""

HISTÓRICO DE OBSERVAÇÕES REGISTRADAS:
"""
${combinedObsText.slice(0, 8000)}
"""

Orientações adicionais:
- Redija o relatório em português brasileiro formal e adequado para pedagogia.
- Preencha cada seção da estrutura de forma rica e detalhada, baseando-se estritamente nos fatos reais descritos nas observações.
- Não invente acontecimentos que não estejam descritos nas observações, mas faça as conexões pedagógicas pertinentes de acordo com as evidências apresentadas.
- Retorne apenas o relatório em formato Markdown estruturado.`;

    let generatedReport = '';
    
    // Tenta OpenRouter
    try {
      console.log('[Compilar Relatório] Chamando OpenRouter...');
      const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
          messages: [{ role: 'user', content: systemPrompt }],
          temperature: 0.5,
          max_tokens: 2500
        })
      });
      if (response.ok) {
        const data = await response.json();
        generatedReport = data.choices?.[0]?.message?.content || '';
      } else {
        console.warn('[Compilar Relatório] Falha no OpenRouter status:', response.status);
      }
    } catch (err) {
      console.error('[Compilar Relatório] Erro no OpenRouter:', err.message);
    }

    // Fallback para Google Gemini
    if (!generatedReport && process.env.GOOGLE_AI_API_KEY) {
      try {
        console.log('[Compilar Relatório] Fallback para Google Gemini...');
        const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash'}:generateContent?key=${process.env.GOOGLE_AI_API_KEY}`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            contents: [{ parts: [{ text: systemPrompt }] }],
            generationConfig: { temperature: 0.5, maxOutputTokens: 2500 }
          })
        });
        if (response.ok) {
          const data = await response.json();
          generatedReport = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
        }
      } catch (err) {
        console.error('[Compilar Relatório] Erro no Google Gemini:', err.message);
      }
    }

    // Fallback para Groq
    if (!generatedReport && process.env.GROQ_API_KEY) {
      try {
        console.log('[Compilar Relatório] Fallback para Groq...');
        const response = await fetch('https://api.groq.com/openai/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.GROQ_MODEL || 'llama-3.3-70b-versatile',
            messages: [{ role: 'user', content: systemPrompt }],
            temperature: 0.5,
            max_tokens: 2500
          })
        });
        if (response.ok) {
          const data = await response.json();
          generatedReport = data.choices?.[0]?.message?.content || '';
        }
      } catch (err) {
        console.error('[Compilar Relatório] Erro no Groq:', err.message);
      }
    }

    if (!generatedReport) {
      throw new Error('Falha ao compilar relatório pedagógico com todos os provedores de IA.');
    }

    res.json({
      report: generatedReport,
      templateNome: template.nome,
      crianca: crianca === 'todas' ? 'Sala de Aula (Relatório Global)' : (crianca || 'Todos os alunos'),
      periodo: (dataInicio || '') + (dataInicio && dataFim ? ' a ' : '') + (dataFim || 'Todo o período'),
      totalObservacoes: observations.length
    });
  } catch (err) {
    console.error('Erro geral ao compilar relatório:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: GET /api/observacoes — lista todas as observações (índice)
app.get('/api/observacoes', requireAuth, async (req, res) => {
  try {
    const dbRes = await dbPool.query("SELECT id, filename, date, time, timestamp, criancas, turma, tags, SUBSTRING(report FROM 1 FOR 120) || '...' as preview FROM escola.observacoes ORDER BY timestamp DESC;");
    res.json({ observations: dbRes.rows });
  } catch (err) {
    console.error('Erro ao listar observações:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: GET /api/observacoes/:yearMonth/:filename — busca relatório específico
app.get('/api/observacoes/:yearMonth/:filename', requireAuth, async (req, res) => {
  const { filename } = req.params;
  try {
    const dbRes = await dbPool.query("SELECT report, date, time, timestamp, criancas, turma, tags FROM escola.observacoes WHERE filename = $1;", [filename]);
    if (dbRes.rows.length === 0) {
      return res.status(404).json({ error: 'Não encontrado' });
    }
    const obs = dbRes.rows[0];
    const mdContent = `---\ndata: "${obs.date}"\nhora: "${obs.time}"\ntimestamp: "${obs.timestamp}"\ncriancas: ${JSON.stringify(obs.criancas)}\nturma: "${obs.turma}"\ntags: ${JSON.stringify(obs.tags)}\n---\n\n${obs.report}`;
    res.setHeader('Content-Type', 'text/markdown');
    res.send(mdContent);
  } catch (err) {
    console.error('Erro ao ler observação do banco:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: GET /api/observacoes/buscar — busca observações por criança e/ou período
app.get('/api/observacoes/buscar', requireAuth, async (req, res) => {
  const { crianca, mes, tags, limit = 20 } = req.query;
  try {
    let sql = "SELECT id, filename, date, time, timestamp, criancas, turma, tags, report as \"fullContent\", SUBSTRING(report FROM 1 FOR 120) || '...' as preview FROM escola.observacoes WHERE 1=1";
    const params = [];
    let pCount = 1;

    if (crianca) {
      sql += ` AND jsonb_typeof(criancas) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(criancas) AS c 
        WHERE LOWER(c) LIKE $${pCount}
      )`;
      params.push(`%${crianca.toLowerCase()}%`);
      pCount++;
    }

    if (mes) {
      sql += ` AND date LIKE $${pCount}`;
      params.push(`${mes}%`);
      pCount++;
    }

    if (tags) {
      const tagList = tags.split(',').map(t => t.trim().toLowerCase());
      sql += ` AND jsonb_typeof(tags) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(tags) AS t 
        WHERE LOWER(t) = ANY($${pCount})
      )`;
      params.push(tagList);
      pCount++;
    }

    sql += ` ORDER BY timestamp DESC LIMIT $${pCount}`;
    params.push(parseInt(limit));

    const dbRes = await dbPool.query(sql, params);
    
    const totalRes = await dbPool.query("SELECT COUNT(*) FROM escola.observacoes;");
    const totalCount = parseInt(totalRes.rows[0].count);

    res.json({ observations: dbRes.rows, total: totalCount });
  } catch (err) {
    console.error('Erro ao buscar observações:', err);
    res.status(500).json({ error: err.message });
  }
});

// Rota: GET /api/observacoes/resumo — compila observações em resumo para documentos
app.get('/api/observacoes/resumo', requireAuth, async (req, res) => {
  const { crianca, mes } = req.query;
  if (!crianca) return res.status(400).json({ error: 'Nome da criança é obrigatório' });

  try {
    let sql = `
      SELECT date, time, report, tags, turma, SUBSTRING(report FROM 1 FOR 120) || '...' as preview 
      FROM escola.observacoes 
      WHERE jsonb_typeof(criancas) = 'array' AND EXISTS (
        SELECT 1 FROM jsonb_array_elements_text(criancas) AS c 
        WHERE LOWER(c) LIKE $1
      )
    `;
    const params = [`%${crianca.toLowerCase()}%`];

    if (mes) {
      sql += " AND date LIKE $2";
      params.push(`${mes}%`);
    }

    sql += " ORDER BY timestamp DESC LIMIT 15";

    const dbRes = await dbPool.query(sql, params);

    if (dbRes.rows.length === 0) {
      return res.json({ resumo: null, total: 0, message: `Nenhuma observação encontrada para "${crianca}"${mes ? ` em ${mes}` : ''}.` });
    }

    const observations = dbRes.rows;
    const texts = observations.map(o => o.report).filter(Boolean);
    const combined = texts.join('\n\n---\n\n');

    const agentConfig = readAgentConfig();
    const agentName = agentConfig.agentName || "Kemily";
    
    const compilePrompt = `Você é a ${agentName}, assistente pedagógica. Compilar um RESUMO PEDAGÓGICO consolidado a partir de várias observações de uma criança.

CRIANÇA: ${crianca}
NÚMERO DE OBSERVAÇÕES: ${observations.length}
${mes ? `PERÍODO: ${mes}` : ''}

COLEÇÃO DE OBSERVAÇÕES:
"""
${combined.slice(0, 4000)}
"""

GERE UM RESUMO ESTRUTURADO COM:

## Perfil da Criança
[Padrões de comportamento, habilidades, desenvolvimento observado]

## Aspectos Sociais e Emocionais
[Interações com outras crianças, gestão emocional, socialização]

## Aspectos Cognitivos e de Aprendizagem
[Curiosidade, resolução de problemas, atenção, concentration]

## Aspectos Motores e de Linguagem
[Desenvolvimento motor, linguagem oral, comunicação]

## Pontos de Atenção
[Dificuldades, necessidades de suporte, áreas a desenvolver]

## Pontos Fortes
[Conquistas, habilidades desenvolvidas, aspectos positivos]

## Sugestões para Planejamento
[Recomendações para próximos passos, atividades sugeridas, seguimento]

-use linguagem formal-pedagógica
-Máximo 300 palavras
-Seja objetivo e baseado nas evidências das observações`;

    const compileRes = await fetch('https://openrouter.ai/api/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
        messages: [{ role: 'user', content: compilePrompt }],
        temperature: 0.4,
        max_tokens: 800
      })
    });

    const compileData = await compileRes.json();
    const resumoText = compileData.choices?.[0]?.message?.content || '';

    res.json({
      crianca,
      mes: mes || 'todos',
      total: observations.length,
      datas: observations.map(o => o.date).filter(Boolean),
      resumo: resumoText,
      observations: observations.map(o => ({
        date: o.date, time: o.time, tags: o.tags, turma: o.turma, preview: o.preview
      }))
    });
  } catch (err) {
    console.error('Erro ao compilar resumo:', err);
    res.status(500).json({ error: 'Erro ao gerar resumo pedagógico' });
  }
});

// Rota POST para upload de imagem em base64
app.post('/api/upload-avatar', requireAuth, (req, res) => {
  const { avatarType, base64Data } = req.body;
  if (!avatarType || !base64Data) {
    return res.status(400).json({ error: 'Dados inválidos' });
  }

  const matches = base64Data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
  if (!matches || matches.length !== 3) {
    return res.status(400).json({ error: 'Formato de imagem inválido' });
  }

  const imageBuffer = Buffer.from(matches[2], 'base64');
  const fileExt = matches[1].split('/')[1] || 'png';
  const fileName = `custom_${avatarType}_avatar_${Date.now()}.${fileExt}`;
  
  // Garantir que a pasta public/assets existe
  const assetsDir = path.join(__dirname, 'public', 'assets');
  if (!fs.existsSync(assetsDir)) {
    fs.mkdirSync(assetsDir, { recursive: true });
  }

  const filePath = path.join(assetsDir, fileName);

  fs.writeFile(filePath, imageBuffer, (err) => {
    if (err) {
      console.error('Erro ao salvar avatar:', err);
      return res.status(500).json({ error: 'Erro ao salvar arquivo' });
    }
    
    // Otimizar a imagem em segundo plano com Python
    const { exec } = require('child_process');
    exec(`python3 scratch/optimize_avatars.py "${filePath}"`, (pyErr) => {
      if (pyErr) {
        console.error('Erro ao otimizar o avatar enviado com Python:', pyErr);
      }
      return res.json({ success: true, url: `assets/${fileName}` });
    });
  });
});

// Rota de login (página)
app.get('/login', (req, res) => {
  const session = req.signedCookies.camila_session || req.cookies.camila_session;
  if (session === 'authenticated') {
    res.redirect('/');
  } else {
    res.sendFile(path.join(__dirname, 'public', 'login.html'));
  }
});

// API de status do modelo
app.get('/api/status', requireAuth, (req, res) => {
  res.json({
    model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
    provider: 'openrouter',
    minimax_available: !!process.env.MINIMAX_API_KEY,
    timestamp: new Date().toISOString()
  });
});

// API para Login
app.post('/api/login', (req, res) => {
  const { password } = req.body;
  if (password === ACCESS_PASSWORD) {
    // Define cookie de sessão assinado que dura 30 dias
    res.cookie('camila_session', 'authenticated', {
      signed: true,
      httpOnly: true,
      maxAge: 30 * 24 * 60 * 60 * 1000, // 30 dias
      sameSite: 'lax',
      secure: process.env.NODE_ENV === 'production'
    });
    res.json({ success: true });
  } else {
    res.status(401).json({ error: 'Senha incorreta' });
  }
});

// API para Logout
app.post('/api/logout', (req, res) => {
  res.clearCookie('camila_session');
  res.json({ success: true });
});

// Proteger arquivos estáticos do diretório public exceto o login
app.use((req, res, next) => {
  if (req.path === '/login' || req.path === '/login.html' || req.path === '/login.js' || req.path === '/style.css' || req.path.startsWith('/assets/')) {
    return next();
  }
  requireAuth(req, res, next);
});

// Servir arquivos estáticos do diretório public
app.use(express.static(path.join(__dirname, 'public')));

// System prompt da Kemily (injetado em todas as conversas)
const KEMILY_SYSTEM_PROMPT = {
  role: "system",
  content: `Você é a Kemily, assistente virtual e extensionista digital da Profª Camila Martella Gasparini Reifonas.

═══════════════════════════════════════════════════
IDENTIDADE — Quem é a Camila (para que você possa falar dela com precisão)
═══════════════════════════════════════════════════

INFORMAÇÕES PESSOAIS:
- Nome completo: Camila Martella Gasparini Reifonas
- Profissão: Professora da Educação Infantil na rede municipal de São Bernardo do Campo, SP

SITUAÇÃO FUNCIONAL / CONTEXTO TEMPORAL:
- **Ano de 2024 (ano passado)**: Professora efetiva de turma fixa, atuava no Infantil III (turma de 4-5 anos) como professora titular da turma.
- **Ano de 2025 (este ano)**: Professora substituta (não efetiva), não está em turma fixa. Atua no projeto Mind Lab como professora substituta, ajudando em atividades e projetos da coordenação.
- Etapa de atuação atual: projeto Mind Lab / substituição na Educação Infantil
- Local de trabalho: EMEB Mariana Neves Interliche — Sala 7, Turma B (contexto 2024)

ESCOLA — EMEB MARIANA NEVES INTERLICHE:
- Tipo: Escola Municipal de Educação Infantil (e Ensino Fundamental)
- Diretora: Maria (conforme documentos da Camila)
- Coordenadoras: Sirlene e Fernanda (coordenação pedagógica)
- Localização: São Bernardo do Campo, SP — região do Grande ABC

REDE MUNICIPAL DE SÃO BERNARDO DO CAMPO:
- Secretaria Municipal de Educação (SME)
- Subsidiado por: BNCC, Proposta Curricular de SBC, documento orientador da rede
- Formadora de referência (2024): Formadora Márcia (formações sobre leitura e escrita)
- Coordenadora de área/volante: Professora Sirlene

**ATENÇÃO - COORDENAÇÃO 2025:**
- Márcia era a formadora de referência em 2024. Para 2025, a coordenação pode ter mudado — não temos informação confirmada sobre quem ocupa esse papel neste ano. Se não souber, diga "não tenho essa informação".

SUPERIORAS/DOCENTES DE REFERÊNCIA (2024):
- Gestão da escola: Maria (diretora)
- Coordenação pedagógica: Sirlene e Fernanda (coordenadoras)
- Formadora da rede: Márcia (2024)
- Professora referenciada para substituição: Professora com quem Camila substitui

QUANDO A CAMILA PERGUNTAR "QUEM É A CAMILA" OU "FALE SOBRE MIM":
Responda com orgulho e detalhes, usando informações dos documentos reais. Exemplo:
"Você é a Profª Camila Martella Gasparini Reifonas, professora substituta da rede municipal de São Bernardo do Campo. Atua no Infantil III, turma B, Sala 7 da EMEB Mariana Neves Interliche. Você é reconhecida pelo seu compromisso com o protagonismo infantil, a escuta ativa e o desenvolvimento integral das crianças. Escreve em estilo formal-pedagógico com calor humano, utilizando expressões como 'colocando as crianças como protagonistas' e 'nos mais diversos contextos'. Trabalha com a metodologia Mind Lab, desenvolvimento fonológico e seguimento da BNCC, sempre priorizando a intencionalidade educativa em suas práticas."

SE NÃO SOUBER ALGUMA INFORMAÇÃO: Diga "Não tenho essa informação nos meus documentos, mas posso te ajudar a encontrar isso" — nunca invente.

═══════════════════════════════════════════════════
PERFIL DA PROFESSORA
═══════════════════════════════════════════════════

- Rede municipal de São Bernardo do Campo, SP
- EMEB Mariana Neves Interliche
- Turma: Infantil III – Turma B, Sala 7
- Faixa etária: crianças de 4-5 anos
- Atuação em substituição (professora substituta)
- Metodologias: Mind Lab (jogos certificados INMETRO), desenvolvimento fonológico, BNCC
- Estilo de escrita: formal-pedagógico com calor humano — frases longas, encadeadas, com posicionamentos pessoais ("ao meu ver", "creio que")

SUA MISSÃO:
Ajudar a Camila na criação de relatórios HTPC, planejamentos quinzenais/mensais, resumos de livros infantis, registros de formação, estudos de caso e documentos pedagógicos diversos.

═══════════════════════════════════════════════════
APRENDIZADO CONTÍNUO — Aprendendo com a Camila
═══════════════════════════════════════════════════

Você pode e DEVE aprender novas informações com a Camila durante nossas conversas.

QUANDO APRENDER algo novo (fato, nome, projeto, detalhe):
- Quando a Camila te ensinar algo, diga: "Anotado! Vou lembrar disso." ou similar
- Em seguida, chame a função para salvar esse conhecimento: o sistema gravará automaticamente

QUANDO NÃO SOUBER algo ou tiver DÚVIDA:
- Pergunte diretamente à Camila: "Camila, pode me ajudar a entender [assunto]? Quem é [pessoa]? O que é [projeto]?"
- Nunca finja que sabe — seja transparente e peça ajuda
- Após a resposta da Camila, o sistema salvará essa informação para consultas futuras

FONTES DE CONHECIMENTO VÁLIDAS:
- Conversas com a Camila (ela te ensina diretamente)
- Documentos que ela compartilhar (enviados por ela ou mencionados)
- Informações da pasta /Agente (dossiers, estilos, projetos)
- NÃO invente informações — se não aprendeu, pergunte

MANEIRA DE PEDIR ESCLARECIMENTO à Camila:
"Percebi que não tenho essa informação nos meus documentos. Você poderia me ensinar sobre [assunto]? Assim consigo te ajudar melhor da próxima vez."

QUANDO APRENDER algo novo da Camila:
Quando ela te ensinar um fato, nome, projeto ou detalhe novo, inclua NO FINAL da sua resposta a tag:
[KNOWLEDGE: o que você aprendeu]

Exemplo: se a Camila disser "a coordenação deste ano está sob responsabilidade da profª Ana", você responde normal e inclui:
[KNOWLEDGE: Coordenação 2025 está sob responsabilidade da profª Ana]

═══════════════════════════════════════════════════
ESTILO DE ESCRITA DA CAMILA — REGRAS RIGOROSAS
═══════════════════════════════════════════════════

A Camila escreve em estilo FORMAL-PEDAGÓGICO COM CALOR HUMANO. Você DEVE seguir todas estas regras ao produzir textos para ela:

1. FRASES LONGAS E ENCADEADAS:
   ✅ "O professor media esse conhecimento prévio, estando atento e propondo experiências significativas."
   ❌ Não use frases curtas e secas. Nunca fragmente o pensamento.

2. VOCABULÁRIO TÉCNICO-PEDAGÓGICO (uso natural, não forçado):
   - protagonismo, intencionalidade educativa, desenvolvimento integral
   - escuta ativa, vivências, saberes prévios, mediação, autorregulação
   - potencialidades, avanços, coerência, propostas
   - campos de experiência, BNCC, etapa do ensino

3. EXPRESSÕES CARACTERÍSTICAS (são MARCA, use com naturalidade):
   - "colocando as crianças como protagonistas"
   - "faz parte do mundo letrado"
   - "nos mais diversos contextos/formatos/portadores"
   - "propiciando assim"
   - "a meu entender" / "ao meu ver" / "creio que"
   - "dessa forma" / "nesse sentido" / "para além de"
   - "a partir de" / "por meio de" / "acerca de"
   - "partilhou" / "socializou" (em vez de "compartilhou")
   - "aguçando interesses e curiosidade"
   - "compartilhada" / "coletiva"

4. ESTRUTURA DE PARÁGRAFO:
   Afirmação inicial → Desenvolvimento com exemplos/conexões → Transição para próximo ponto

5. CENTRALIDADE NA CRIANÇA:
   Sempre volte ao estudante como protagonista do processo ensino-aprendizagem.

6. POSICIONAMENTO PESSOAL:
   Use "Considero que...", "Entendo que...", "Creio que...", "Ao meu ver..." para se posicionar.

7. O QUE EVITAR:
   - ❌ "de acordo com" → use "conforme", "segundo", "a partir de"
   - ❌ "no dia a dia" → use "cotidianamente", "no cotidiano"
   - ❌ "em relação a" → use "sobre", "acerca de", "a respeito de"
   - ❌ fragmentação em frases curtas
   - ❌ gírias ou linguagem informal
   - ❌ linguagem burocrática vazia

═══════════════════════════════════════════════════
EXEMPLOS REAIS DA ESCRITA DA CAMILA
═══════════════════════════════════════════════════

EXEMPLO 1 — Relatório HTPC (avaliação anual):
"O professor como observador, faz escuta ativa da criança, para compreender o que mais chama atenção da turma, leva para a roda de conversa, aponta as curiosidades e assim consegue planejar e executar as práticas pedagógicas, colocando as crianças como protagonistas do processo ensino-aprendizagem. O planejamento se baseia principalmente nos interesses das crianças, trazendo situações que desafiam e aguçam seus interesses e curiosidade."

EXEMPLO 2 — Carta de Intenções:
"Inicio esta carta de intenções para o ano de 2026 com esta citação atribuída à escritora Lya Luft, pois reconheço claramente que a organização e o atendimento estabelecidos na Educação Infantil atuam como fundamentos desta etapa basilar da vida humana, merecendo especial atenção, dos aspectos simples aos mais complexos, e da qualidade deste trabalho dependem as fases posteriores de crescimento, aprendizagem, amadurecimento e desenvolvimento integral de cada criança."

EXEMPLO 3 — Planejamento quinzenal:
"Durante a visita ao mercadinho, pretendo propor que as crianças criem uma salad nutritiva, a partir dos vegetais que encontrarem nos setores de hortifruti, latarias e congelados, verificando o preço desses alimentos, e posteriormente faremos uma listagem verificando quais tipos de vitaminas e sais minerais encontraremos nessa salada e sua funcionalidade para o organismo."

EXEMPLO 4 — Registro de formação:
"Fomos divididas nos seguintes grupos: Grupo 1 — Viviane, Camila, Patrícia e Vanessa: jogos estruturados, conversamos sobre apresentar jogos de chão para ir trabalhando regras gerais com as crianças, para depois utilizar material ATTO e Mind Lab com regras mais estabelecidas, cuidando das peças, utilizar Lego, repertoriar as crianças, deixar explorar o material utilizando peças maiores, deixar as crianças criando por um momento livres e depois contextualizando as montagens com alguma história."

═══════════════════════════════════════════════════
CONHECIMENTO ESPECÍFICO DA CAMILA
═══════════════════════════════════════════════════

PROJETOS E VIVÊNCIAS DA ESCOLA:
- Entradas coletivas (com presentations de poesias)
- Atividades intersalas (jogos de tabuleiro, brincadeiras coletivas, tema)
- Piquenique literário (contração de histórias pelas crianças do Infantil IV e V)
- Saraus de poesia
- Painéis coletivos e murais
- Dia da família na escola
- Sábado letivo (apresentações de turmas para famílias)
- Estudo do meio (ex: Borboletário de Diadema)
- Mercadinho (sala 4 — trabalho com números, preços, lista)
- Projeto de leitura (empréstimo de livros da BEI)
- Show de acrobacias, baile à fantasia, cabelo maluco
- Teatro de bonecos, cineminha com pipoca
- Gincana, cataventos

MOMENTOS DO HTPC:
- "Nutrição" (momento inicial com livro/vídeo compartilhado)
- Frase do dia
- Momento da brincadeira
- Pauta da reunião
- Grupos de discussão
- Encerramento com registro

LIVROS INFANTIS CONHECIDOS:
- "Shhh! Nós temos um plano" (Chris Haughton)
- "A Galinha Ruiva" (Byron Barton)
- "Marcelo, Marmelo, Martelo" (Ruth Rocha)
- "O Ponto" (Peter H. Reynolds)
- "O Fabricante de Lápis"
- "A Primeira Infância" (vídeo)
- "Como os doces brasileiros ficaram tão doces" (vídeo)

FORMADORAS/MENTORAS:
- Formadora Márcia (formações sobre leitura e escrita)
- Sirlene (coordenadora)
- Fernanda (coordenadora)
- Maria (gestora)

METODOLOGIA:
- Mind Lab (jogos certificados INMETRO: Tik Tak Toy, Hora do Rush, Caixa Mágica, Resta-Um, Quatro-em-linha)
- Material ATTO (durável, para jogos de chão)
- Brincadeiras musicais e cantigas de roda
- Contação de histórias
- Desenvolvimento fonológico (rimas, ritmo)
- Desenvolvimento sintático (estruturação de frases)
- Desenvolvimento semântico (significados e vocabulário)
- Desenvolvimento pragmático (uso social da linguagem)
- Professor como escriba

BNCC/CAMPOS DE EXPERIÊNCIA:
- O eu, o outro e o nós
- Corpo, gestos e movimentos
- Traços, sons, cores e formas
- Escuta, fala, pensamento e imaginação
- Espaços, tempos, quantidades, relações e transformações

ESCRITA DA CRIANÇA:
- Hipótese de escrita silábico-alfabética
- Hipótese de escrita alfabética

═══════════════════════════════════════════════════
SISTEMA DE OBSERVAÇÕES POR VOZ — Como acessar registros
═══════════════════════════════════════════════════

A Camila possui um sistema de OBSERVAÇÕES POR VOZ onde grava registros sobre crianças durante o dia. Esses registros são salvos e ficam disponíveis para consulta.

QUANDO A CAMILA PEDIR "observações da [nome da criança]", "resumo da [criança]", "ler observações da [nome]" ou similar:
→ Use a API: GET /api/observacoes/resumo?crianca=[nome]&mes=YYYY-MM
→ O sistema retorna um RESUMO PEDAGÓGICO compilado de todas as observações daquela criança no período
→ Use esse resumo para ajudar a elaborar HTPCs, relatórios, planejamentos

EXEMPLOS DE COMANDO:
- "mostre as observações da Maria deste mês"
- "resumo pedagógico da Ana"
- "ler observações do João"
- "compilar observações da Sofia em maio"

AO RECEBER O RESUMO:
- Analise o conteúdo returned
- Use para fundamentar documentos que a Camila pedir
- Se não houver observações, diga: "Não encontrei observações registradas para [criança] neste período. Posso ajudar de outra forma?"
- Integre o resumo de forma natural no documento que estiver criando

LIMITES:
- Máximo 15 observações por consulta
- Resumos são compactados para caber no contexto
- Se a Camila pedir mais detalhes, pode mostrar a lista de observações individuais

═══════════════════════════════════════════════════
REGRAS DE ATUAÇÃO
═══════════════════════════════════════════════════

1. Quando a Camila pedir um documento (relatório, planejamento, etc):
   - Seguir ESTRITAMENTE o estilo de escrita dela
   - Usar frases longas e encadeadas
   - Incluir vocabulário técnico-pedagógico natural
   - Posicionar-se pessoalmente quando apropriado
   - Manter centralidade na criança

2. Quando pedir resumo de livro infantil:
   - Identificar temas, personagens, lições
   - Sugerir atividades práticas de sala
   - Conectar com BNCC e campos de experiência

3. Quando pedir pesquisa pedagógica:
   - Fundamentar com referências reais
   - Dar exemplos práticos conectando com a realidade da turma

4. Para relatórios HTPC, usar estrutura:
   - Planejamento e execução das práticas pedagógicas
   - Coerência entre propostas e desenvolvimento
   - Diversidade de experiências e intencionalidade educativa
   - Avaliação e acompanhamento das aprendizagens

5. Nunca inventar nomes de crianças, atividades ou datas. Use apenas o que conhece dos documentos.

6. Referências: usar "conforme a BNCC", "segundo a Proposta Curricular de SBC", "a partir do documento orientador".

7. Se perguntarem sobre a Camila (quem ela é, onde trabalha, quem são suas superiores, etc): use as informações da seção IDENTIDADE. Se não souber algo, diga claramente que não tem a informação — nunca invente.

═══════════════════════════════════════════════════════════════════
ESTILO DOS RELATÓRIOS SEMESTRAIS — Como analisar e compor
═══════════════════════════════════════════════════════════════════

A Camila produz RELATÓRIOS SEMESTRAIS em um estilo pessoal, narrativo, 
positivo e descritivo — diferente dos modelos institucionais. Você DEVE 
seguir esse estilo quando a Camila pedir ajuda para analisar observações 
ou compor relatórios.

Estrutura de um relatório semestral completo:

1. INTRODUÇÃO COMUM (um texto só, para TODOS os alunos)
   → Refere-se à turma ("as crianças", "a turma", "foram realizadas")
   → Não menciona nomes de alunos
   → Cobre: planejamento pedagógico, projetos, atividades coletivas
   → Exemplo de abertura: "Em continuidade ao trabalho iniciado no 
     semestre anterior, o planejamento das ações em relação às crianças 
     visa favorecer o desenvolvimento físico, cognitivo, afetivo e 
     emocional..."

2. RELATÓRIO INDIVIDUAL DO ALUNO (um texto por aluno)
   → Sempre inicia: "[Nome] apresentou muitos avanços neste período."
   → Estrutura interna em blocos temáticos (sem numeração, sem títulos)
   → Finaliza com esperança/prognóstico para o próximo período

3. FECHAMENTO (obrigatório, padrão para todos os alunos)
   → "Espero que o/a [nome] continue se interessando bastante pela 
      escola e avançando muito em suas aprendizagens 
      [no próximo ano / e potencialidades no próximo ano]"

ESTRUTURA INTERNA DO RELATÓRIO INDIVIDUAL (blocos implícitos):

  Bloco 1 — Abertura
    "[Nome] apresentou muitos avanços neste período."

  Bloco 2 — Brincadeiras e Preferências
    Locomoção e playground, materiais preferidos, simbolismo nas 
    brincadeiras (faz-de-conta)
    Exemplo: "Gosta de utilizar objetos como potes e pazinhas para criar 
    formas com areia e simbolizar brincadeiras com esses elementos."

  Bloco 3 — Interações Sociais
    Melhores amigos (nomes entre aspas: "o Izack", "a Mirella"), 
    dinâmicas de grupo, resolução de conflitos
    Exemplo: "Seus melhores amigos no momento são a Mirella, o Natan 
    e a Cecília, mas sem prejuízo nas interações com os outros colegas."

  Bloco 4 — Desenvolvimento Motor e Corporal
    Habilidades físicas, regras e combinados, atividades coletivas
    Exemplo: "Demonstrou bastante agilidade nas brincadeiras envolvendo 
    cordas de pular, triciclos e circuitos com materiais diversificados."

  Bloco 5 — Linguagem, Oralidade e Comunicação
    Vocabulário, clareza, participação em recitação/histórias/música
    Exemplo: "Demonstra um bom vocabulário e expressa suas ideias 
    verbalmente com bastante clareza, sendo capaz de relacionar os 
    fatos em sequência e com coerência."

  Bloco 6 — Criatividade e Atividades Diversificadas
    Brinquedos, jogos, construções, arte
    Exemplo: "Nas atividades diversificadas tem preferido utilizar o 
    lego e peças plásticas, e produz formas relacionadas a casas, 
    prédios e torres."

  Bloco 7 — Autonomia e Cuidados (quando aplicável)
    Organização do material, alimentação, porcionamento
    Exemplo: "Ampliou sua preferência pelas carnes, frutas, verduras 
    e legumes, necessitando de orientação diária para porcionar 
    corretamente, evitando o desperdício."

  Bloco 8 — Desafios para o próximo período (se houver)
    FORMULAR SEMPRE com linguagem atenuada
    Use: "necessitando de", "permanece o desafio de", "algumas vezes"
    Nunca critique diretamente — sempre contextualizar
    Exemplo: "necessitando de intervenção dos adultos para que respeite 
    seu momento de participar, organizando-se em fila"
    Exemplo: "permanece para o próximo ano o desafio de esclarecer e 
    retomar comandas das propostas a serem realizadas"

  Bloco 9 — Fechamento (obrigatório)
    "Espero que o/a [nome] continue se interessando bastante pela 
    escola e avançando muito em suas aprendizagens no próximo ano."

VOCABULÁRIO E EXPRESSÕES CARACTERÍSTICAS:

  TERMOS QUE VOCÊ DEVE USAR:
  • "apresentou muitos avanços" → abertura (SEMPRE)
  • "demonstrou" → para qualidades observadas (nunca "mostrou")
  • "gosta de" / "gostava de" → preferências
  • "participou de" → atividades coletivas
  • "avançou" / "evoluiu" → progresso
  • "interações" → relação entre crianças
  • "brincadeiras simbólicas" → faz-de-conta
  • "vivências lúdicas" → experiências de aprendizagem
  • "espirito desportivo" → fair play
  • "trabalho coletivo" → cooperação
  • "cultura de paz" → resolução de conflitos
  • "sensibilidade e afetividade" → empatia
  • "concentração e foco" → atenção
  • "combidados" → regras da turma (nunca diga "regras" sozinho)
  • "porcionar" → para servings de comida
  • "ampliou sua preferência por" → alimentação

  EXPRESSÕES CARACTERÍSTICAS:
  • "a todo momento"
  • "juntos com" (nunca "junto com")
  • "de acordo com o que lhe é proposto"
  • "especialmente" → para destacar pontos marcantes
  • "demonstrando" → ações que evidenciam qualidades
  • "algumas vezes" → atenuar críticas
  • "assim como", "bem como" → conectivos

  O QUE NUNCA USAR:
  • "mostrou" (use "demonstrou")
  • "acordos" (use "combidados")
  • "regras" isoladamente (use "regras e combinados")
  • Críticas diretas ou palavras como "problema", "dificuldade"
  • Menção a condições de saúde ou situações familiares

  ESTILO DE FRASE:
  • Frases longas e descritivas, com vírgulas separando subordinadas
  • Predominância de verbos no pretérito imperfeito e perfeito
  • Nunca frases curtas e secas

EXEMPLO DE ABERTURA DO RELATÓRIO INDIVIDUAL (Alice):
"Alice apresentou muitos avanços neste período. Demonstra muita 
autonomia em todas as tarefas referentes à rotina escolar, na 
realização das atividades propostas, nos cuidados com o material de 
uso individual e coletivo, na organização e classificação dos 
brinquedos durante e após a sua utilização. Gosta de brincar nos 
balanços, e se entretém bastante com os potes e pazinhas na areia, 
criando muitas brincadeiras simbólicas utilizando esses materiais."

EXEMPLO DE FECHAMENTO DO RELATÓRIO INDIVIDUAL:
"Espero que a Alice continue se interessando bastante pela escola e 
avançando muito em suas aprendizagens no próximo ano."
"Espero que o Theo continue se interessando bastante pela escola e 
avançando muito em suas aprendizagens e potencialidades no próximo ano."

QUANDO A CAMILA PEDIR PARA COMPOR UM RELATÓRIO SEMESTRAL:
1. Analise as observações coletadas no período
2. Extraia FACTS puros: comportamentos observados (não interpretações)
3. Classifique cada fato em uma das 8 dimensões acima
4. Selecione os 6-8 fatos mais significativos
5. Compõe o relatório seguindo a estrutura narrativa da Camila
6. Use frases longas e encadeadas, vocabulário pedagógico natural
7. Desafios/formulações negativas → use linguagem atenuada

Respond SEMPRE em português do Brasil.`

};

// Rota de Chat com OpenRouter com Smart Router Multimodal integrado (MiniMax)
app.post('/api/chat', requireAuth, async (req, res) => {
  const { messages, forcedIntent } = req.body;

  if (!messages || !Array.isArray(messages)) {
    return res.status(400).json({ error: 'Mensagens inválidas' });
  }

  const lastMessage = messages[messages.length - 1];
  const promptText = lastMessage ? lastMessage.content : '';

  // 1. Detectar se há imagens anexadas para desviar para OpenRouter multimodal (Vision)
  const hasAttachedImages = promptText.includes('*[Imagem anexada:');

  let intent = forcedIntent || 'TEXT';

  // 1.1 Validações cruzadas de coerência com a intenção forçada
  if (forcedIntent && promptText) {
    if (forcedIntent === 'IMAGE' && /música|musica|cantiga|canção|cancao|trilha|áudio|audio|ouvir|som|melodia/i.test(promptText)) {
      res.setHeader('Content-Type', 'text/event-stream');
      res.setHeader('Cache-Control', 'no-cache');
      res.setHeader('Connection', 'keep-alive');
      res.write(`data: ${JSON.stringify({ content: "Ops! Você digitou uma solicitação de música/áudio, mas clicou no botão **Criar Imagem** (🎨). Para compor uma música, por favor, digite sua solicitação e clique no botão **Criar Música** (🎵)!" })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
    if (forcedIntent === 'AUDIO' && /imagem|desenhe|foto|ilustração|ilustracao|ilustre|desenho|figura|vídeo|video|animação|animacao/i.test(promptText)) {
      const isVideo = /vídeo|video|animação|animacao/i.test(promptText);
      res.setHeader('Content-Type', 'text/event-stream');
      res.setHeader('Cache-Control', 'no-cache');
      res.setHeader('Connection', 'keep-alive');
      res.write(`data: ${JSON.stringify({ content: `Ops! Você digitou uma solicitação de ${isVideo ? 'vídeo' : 'imagem'}, mas clicou no botão **Criar Música** (🎵). Para gerar um ${isVideo ? 'vídeo' : 'desenho/imagem'}, por favor, clique no botão correspondente (**${isVideo ? 'Criar Vídeo 🎬' : 'Criar Imagem 🎨'}**)!` })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
    if (forcedIntent === 'VIDEO' && /música|musica|cantiga|canção|cancao|trilha|áudio|audio|ouvir|som|melodia|imagem|desenhe|foto|ilustração/i.test(promptText)) {
      const isMusic = /música|musica|cantiga|canção|cancao/i.test(promptText);
      res.setHeader('Content-Type', 'text/event-stream');
      res.setHeader('Cache-Control', 'no-cache');
      res.setHeader('Connection', 'keep-alive');
      res.write(`data: ${JSON.stringify({ content: `Ops! Você digitou uma solicitação de ${isMusic ? 'música' : 'imagem'}, mas clicou no botão **Criar Vídeo** (🎬). Para realizar essa tarefa, por favor, clique no botão **${isMusic ? 'Criar Música 🎵' : 'Criar Imagem 🎨'}**!` })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
  }

  // Executar classificação automática apenas se não houver intenção forçada, não houver imagens anexadas e houver prompt
  if (!forcedIntent && !hasAttachedImages && promptText && (process.env.OPENROUTER_API_KEY || process.env.GROQ_API_KEY)) {
    try {
      const systemDetector = "Você é um classificador de intenções de prompt. Classifique a última mensagem do usuário em uma destas quatro categorias exatas: 'TEXT' (para conversas normais, perguntas, redação de relatórios, planejamentos, textos pedagógicos, respostas gerais), 'IMAGE' (se o usuário estiver pedindo explicitamente para gerar, desenhar, criar ou ilustrar uma imagem, figura, ilustração ou foto), 'AUDIO' (se o usuário estiver pedindo explicitamente para gerar uma música, melodia, trilha sonora, cantiga ou som), ou 'VIDEO' (se o usuário estiver pedindo explicitamente para gerar ou criar um vídeo ou animação). Responda APENAS com a palavra da categoria em letras maiúsculas: TEXT, IMAGE, AUDIO, VIDEO.";

      let detectResponse;
      let usingGroqForDetection = false;

      try {
        detectResponse = await fetch('https://openrouter.ai/api/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
            messages: [
              { role: 'system', content: systemDetector },
              { role: 'user', content: promptText }
            ],
            max_tokens: 20,
            temperature: 0.0
          })
        });
      } catch (err) {
        console.error('[Smart Router] Falha de conexão com OpenRouter para detecção, tentando Groq...', err.message);
      }

      // Se OpenRouter falhou ou retornou erro (ex: status != 200), tenta o Groq como fallback
      if ((!detectResponse || !detectResponse.ok) && process.env.GROQ_API_KEY) {
        console.log('[Smart Router] Usando Groq (llama-3.1-8b-instant) para classificar intenção...');
        try {
          detectResponse = await fetch('https://api.groq.com/openai/v1/chat/completions', {
            method: 'POST',
            headers: {
              'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({
              model: 'llama-3.1-8b-instant',
              messages: [
                { role: 'system', content: systemDetector },
                { role: 'user', content: promptText }
              ],
              max_tokens: 20,
              temperature: 0.0
            })
          });
          usingGroqForDetection = detectResponse.ok;
        } catch (groqErr) {
          console.error('[Smart Router] Falha de detecção na API da Groq:', groqErr.message);
        }
      }

      if (detectResponse && detectResponse.ok) {
        const detectData = await detectResponse.json();
        const detectedText = detectData.choices?.[0]?.message?.content?.toUpperCase() || 'TEXT';
        console.log(`[Smart Router] Resposta crua do detector: "${detectedText}"`);

        if (detectedText.includes('IMAGE')) {
          intent = 'IMAGE';
        } else if (detectedText.includes('AUDIO') || detectedText.includes('MUSIC')) {
          intent = 'AUDIO';
        } else if (detectedText.includes('VIDEO')) {
          intent = 'VIDEO';
        } else {
          intent = 'TEXT';
        }
        console.log(`[Smart Router] Intenção detectada para o prompt "${promptText.substring(0, 40)}...": ${intent}`);
      } else {
        const errText = await detectResponse.text();
        console.error('[Smart Router] Chamada de detecção retornou erro:', errText);
      }
    } catch (err) {
      console.error('Erro no classificador de intenções (usando fallback TEXT):', err);
    }
  }

  // ==========================================================================
  // FLUXO DE IMAGEM (MiniMax image-01)
  // ==========================================================================
  if (intent === 'IMAGE') {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Iniciando geração de imagem com o OpenRouter...' })}\n\n`);

    try {
      // Prompt rewriting: pedir à IA para enriquecer o prompt em inglês
      let enrichedPrompt = promptText;
      try {
        const rewriteResp = await fetch('https://openrouter.ai/api/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
            messages: [
              { role: 'system', content: "Reescreva o pedido de imagem do usuário em inglês como um prompt altamente detalhado para geradores de imagem (como Midjourney/Stable Diffusion). Adicione estilo de ilustração infantil fofa, vibrante, adequada para educação infantil, traço limpo, cores harmoniosas, sem textos/letras na imagem. Retorne apenas a tradução/prompt em inglês." },
              { role: 'user', content: promptText }
            ],
            max_tokens: 150,
            temperature: 0.7
          })
        });
        if (rewriteResp.ok) {
          const rewriteData = await rewriteResp.json();
          enrichedPrompt = rewriteData.choices?.[0]?.message?.content?.trim() || promptText;
          console.log('[Smart Router] Prompt reescrito para imagem:', enrichedPrompt);
        }
      } catch (err) {
        console.error('Erro ao reescrever prompt de imagem:', err);
      }

      res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Gerando ilustração pedagógica com o OpenRouter...' })}\n\n`);

      const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: 'x-ai/grok-imagine-image-quality',
          messages: [
            { role: 'user', content: enrichedPrompt }
          ],
          modalities: ['image']
        })
      });

      if (!response.ok) {
        const errText = await response.text();
        throw new Error(`OpenRouter respondeu com erro ao gerar imagem: ${errText}`);
      }

      const imgData = await response.json();
      const imageObj = imgData.choices?.[0]?.message?.images?.[0];
      let imageUrl = imageObj?.image_url?.url;

      if (!imageUrl) {
        throw new Error('Nenhuma imagem foi retornada pelo OpenRouter');
      }

      // Se for base64, salvar localmente no servidor
      if (imageUrl.startsWith('data:')) {
        const fs = require('fs');
        const mediaDir = path.join(__dirname, 'public', 'generated-media');
        if (!fs.existsSync(mediaDir)) {
          fs.mkdirSync(mediaDir, { recursive: true });
        }
        
        const matches = imageUrl.match(/^data:image\/([A-Za-z+-]+);base64,(.+)$/);
        if (matches && matches.length === 3) {
          const ext = matches[1];
          const base64Data = matches[2];
          const fileName = `imagem_${Date.now()}.${ext === 'jpeg' ? 'jpg' : ext}`;
          const filePath = path.join(mediaDir, fileName);
          fs.writeFileSync(filePath, Buffer.from(base64Data, 'base64'));
          imageUrl = `/generated-media/${fileName}`;
          console.log(`[Smart Router] Imagem salva localmente em: ${imageUrl}`);
        }
      }

      res.write(`data: ${JSON.stringify({ type: 'image', url: imageUrl, originalPrompt: promptText })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();

    } catch (error) {
      console.error('Erro na geração de imagem:', error);
      res.write(`data: ${JSON.stringify({ content: `Ocorreu um erro ao gerar a imagem: ${error.message}` })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
  }

  // ==========================================================================
  // FLUXO DE ÁUDIO/MÚSICA (Mensagem Pedagógica Amigável - MiniMax Desativado)
  // ==========================================================================
  // ==========================================================================
  // FLUXO DE ÁUDIO/MÚSICA (google/lyria-3-clip-preview)
  // ==========================================================================
  if (intent === 'AUDIO') {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Compondo e gerando música pedagógica com Lyria AI...' })}\n\n`);

    try {
      if (!process.env.OPENROUTER_API_KEY) {
        throw new Error('OPENROUTER_API_KEY não configurada no servidor.');
      }

      const response = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'HTTP-Referer': 'https://camila.reifonas.cloud',
          'X-Title': 'Camila ChatGPT replica',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: 'google/lyria-3-clip-preview',
          messages: [
            { role: 'user', content: promptText }
          ],
          modalities: ['audio'],
          stream: true
        })
      });

      if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Erro na API do OpenRouter (Lyria): ${errorText}`);
      }

      const reader = response.body.getReader();
      const decoder = new TextDecoder('utf-8');
      let buffer = '';
      let audioBase64Chunks = [];
      let textContent = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop();

        for (const line of lines) {
          const cleanLine = line.trim();
          if (cleanLine.startsWith('data: ')) {
            const dataContent = cleanLine.slice(6);
            if (dataContent === '[DONE]') continue;
            try {
              const parsed = JSON.parse(dataContent);
              const delta = parsed.choices?.[0]?.delta;
              if (delta) {
                if (delta.content) {
                  textContent += delta.content;
                  res.write(`data: ${JSON.stringify({ content: delta.content })}\n\n`);
                }
                if (delta.audio?.data) {
                  audioBase64Chunks.push(delta.audio.data);
                }
              }
            } catch (e) {
              // Ignorar
            }
          }
        }
      }

      // Processar restante do buffer
      if (buffer) {
        const cleanLine = buffer.trim();
        if (cleanLine.startsWith('data: ')) {
          const dataContent = cleanLine.slice(6);
          if (dataContent !== '[DONE]') {
            try {
              const parsed = JSON.parse(dataContent);
              const delta = parsed.choices?.[0]?.delta;
              if (delta) {
                if (delta.content) {
                  textContent += delta.content;
                  res.write(`data: ${JSON.stringify({ content: delta.content })}\n\n`);
                }
                if (delta.audio?.data) {
                  audioBase64Chunks.push(delta.audio.data);
                }
              }
            } catch (e) {
              // Ignorar
            }
          }
        }
      }

      if (audioBase64Chunks.length > 0) {
        const fullAudioBase64 = audioBase64Chunks.join('');
        
        // Salvar em arquivo no servidor
        const fs = require('fs');
        const mediaDir = path.join(__dirname, 'public', 'generated-media');
        if (!fs.existsSync(mediaDir)) {
          fs.mkdirSync(mediaDir, { recursive: true });
        }
        const fileName = `musica_${Date.now()}.mp3`;
        const filePath = path.join(mediaDir, fileName);
        fs.writeFileSync(filePath, Buffer.from(fullAudioBase64, 'base64'));
        
        const audioUrl = `/generated-media/${fileName}`;
        console.log(`[Smart Router] Música salva localmente em: ${audioUrl}`);
        // Envia a mídia estruturada para o frontend
        res.write(`data: ${JSON.stringify({ type: 'audio', url: audioUrl, originalPrompt: promptText })}\n\n`);
      } else {
        // Fallback se não retornou áudio (apenas texto)
        if (textContent) {
          res.write(`data: ${JSON.stringify({ content: '\n\n*(Nota: O áudio não pôde ser gerado por limitações do modelo, mas a letra acima foi composta com carinho!)*' })}\n\n`);
        } else {
          throw new Error('Nenhum dado de áudio ou texto foi retornado pelo modelo Lyria.');
        }
      }

      res.write('data: [DONE]\n\n');
      return res.end();

    } catch (error) {
      console.error('Erro na resposta de áudio com Lyria:', error);
      res.write(`data: ${JSON.stringify({ content: `Desculpe, ocorreu um erro ao gerar a música: ${error.message}. No entanto, se você quiser, eu posso compor uma letra pedagógica sobre o assunto para você!` })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
  }

  // ==========================================================================
  // FLUXO DE VÍDEO (google/veo-3.1-lite via OpenRouter)
  // ==========================================================================
  if (intent === 'VIDEO') {
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Iniciando geração de vídeo com o modelo google/veo-3.1-lite...' })}\n\n`);

    try {
      // 1. Criar o job de geração de vídeo no OpenRouter
      const createResponse = await fetch('https://openrouter.ai/api/v1/videos', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: 'google/veo-3.1-lite',
          prompt: promptText
        })
      });

      if (!createResponse.ok) {
        const errorText = await createResponse.ok ? '' : await createResponse.text();
        throw new Error(`Falha ao iniciar geração de vídeo no OpenRouter: ${errorText || createResponse.statusText}`);
      }

      const jobData = await createResponse.json();
      const jobId = jobData.id;
      const pollingUrl = jobData.polling_url || `https://openrouter.ai/api/v1/videos/${jobId}`;

      console.log(`[Smart Router] Job de vídeo iniciado no OpenRouter com ID: ${jobId}`);
      res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Vídeo enfileirado no OpenRouter, aguardando processamento...' })}\n\n`);

      // 2. Polling loop para monitorar a conclusão do job
      let status = 'queued';
      let videoUrl = null;
      let attempts = 0;
      const maxAttempts = 30; // 3 minutos no total (6s por tentativa)

      while (attempts < maxAttempts) {
        attempts++;
        // Aguarda 6 segundos antes de verificar novamente
        await new Promise(resolve => setTimeout(resolve, 6000));

        console.log(`[Smart Router] Verificando status do job de vídeo ${jobId} (Tentativa ${attempts})...`);

        const statusResponse = await fetch(pollingUrl, {
          headers: {
            'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`
          }
        });

        if (!statusResponse.ok) {
          console.error(`[Smart Router] Falha ao consultar status do job de vídeo: ${statusResponse.statusText}`);
          continue;
        }

        const statusData = await statusResponse.json();
        status = statusData.status;

        if (status === 'completed') {
          videoUrl = statusData.unsigned_urls?.[0];
          break;
        } else if (status === 'failed') {
          throw new Error('A geração do vídeo foi marcada como falha no OpenRouter.');
        } else {
          // in_progress / queued / processing
          const progressPercent = Math.round((attempts / maxAttempts) * 100);
          res.write(`data: ${JSON.stringify({ type: 'media_status', message: `Processando vídeo no OpenRouter (${progressPercent}%)...` })}\n\n`);
        }
      }

      if (!videoUrl) {
        throw new Error('Tempo limite esgotado para a geração do vídeo no OpenRouter.');
      }

      res.write(`data: ${JSON.stringify({ type: 'media_status', message: 'Vídeo concluído! Baixando arquivo para o servidor...' })}\n\n`);

      // 3. Baixar o arquivo MP4 e salvar localmente
      const videoFetch = await fetch(videoUrl);
      if (!videoFetch.ok) {
        throw new Error('Falha ao efetuar download do arquivo de vídeo temporário');
      }

      const videoBuffer = await videoFetch.arrayBuffer();
      const fs = require('fs');
      const mediaDir = path.join(__dirname, 'public', 'generated-media');
      if (!fs.existsSync(mediaDir)) {
        fs.mkdirSync(mediaDir, { recursive: true });
      }

      const fileName = `video_${Date.now()}.mp4`;
      const filePath = path.join(mediaDir, fileName);
      fs.writeFileSync(filePath, Buffer.from(videoBuffer));

      const localVideoUrl = `/generated-media/${fileName}`;
      console.log(`[Smart Router] Vídeo salvo localmente em: ${localVideoUrl}`);

      res.write(`data: ${JSON.stringify({ type: 'video', url: localVideoUrl, originalPrompt: promptText })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();

    } catch (error) {
      console.error('Erro na geração de vídeo:', error);
      res.write(`data: ${JSON.stringify({ content: `Desculpe, ocorreu um erro ao gerar o vídeo: ${error.message}.` })}\n\n`);
      res.write('data: [DONE]\n\n');
      return res.end();
    }
  }

  // ==========================================================================
  // FLUXO PADRÃO DE TEXTO (OpenRouter com SSE streaming original, com fallback Groq)
  // ==========================================================================
  const agentConfig = readAgentConfig();
  const agentName = agentConfig.agentName || "Kemily";
  const temperaturePreset = agentConfig.temperaturePreset || 'equilibrado';
  const temperature = TEMPERATURE_PRESETS[temperaturePreset]?.value || 0.6;

  const knowledgeText = getKnowledgeAsText();

  // 1.2 Integração Dinâmica de Observações no Chat da IA
  let observationsContext = '';
  try {
    const idx = readObsIndex();
    const allChildren = new Set();
    (idx.observations || []).forEach(o => {
      if (o.criancas) {
        o.criancas.forEach(c => allChildren.add(c.trim()));
      }
    });

    const mentionedChildren = [];
    const promptLower = promptText.toLowerCase();
    
    for (const child of allChildren) {
      const regex = new RegExp(`\\b${child.toLowerCase()}\\b`, 'i');
      if (regex.test(promptLower)) {
        mentionedChildren.push(child);
      }
    }

    if (mentionedChildren.length > 0) {
      // Verificar palavras-chave relacionadas à busca de observações
      const askKeywords = ['observa', 'registro', 'historico', 'histórico', 'resumo', 'relatorio', 'relatório', 'desenvolvimento', 'evolução', 'evolucao', 'comportamento', 'como foi', 'como está', 'como esta', 'pedagó'];
      const isAskingAboutObs = askKeywords.some(kw => promptLower.includes(kw));

      if (isAskingAboutObs) {
        let compiledObservations = [];
        for (const child of mentionedChildren) {
          const childObs = (idx.observations || []).filter(o => 
            o.criancas && o.criancas.some(c => c.toLowerCase() === child.toLowerCase())
          );
          
          if (childObs.length > 0) {
            // Carrega as últimas 5 observações
            const lastObs = childObs.slice(0, 5);
            const obsTexts = [];
            for (const o of lastObs) {
              const filePath = path.join(OBSERVACOES_DIR, o.date?.slice(0, 7), o.filename);
              if (fs.existsSync(filePath)) {
                let content = fs.readFileSync(filePath, 'utf8');
                content = content.replace(/^---[\s\S]*?---\n/, '').trim();
                obsTexts.push(`- Data: ${o.date} (${o.time}):\n${content}`);
              }
            }
            if (obsTexts.length > 0) {
              compiledObservations.push(`### Observações de ${child}:\n${obsTexts.join('\n\n')}`);
            }
          }
        }

        if (compiledObservations.length > 0) {
          observationsContext = `\n\n════════════════════════════════════════\nOBSERVAÇÕES REGISTRADAS SOBRE AS CRIANÇAS CITADAS:\n${compiledObservations.join('\n\n')}\nUse as observações acima para responder à pergunta da Camila sobre o desenvolvimento/comportamento da criança de forma precisa e baseada em evidências.\n════════════════════════════════════════\n`;
          console.log(`[Smart Router] Injetando contexto de observações para:`, mentionedChildren);
        }
      }
    }
  } catch (obsErr) {
    console.error('Erro ao integrar observações no prompt da IA:', obsErr);
  }

  const dynamicSystemPrompt = {
    role: "system",
    content: KEMILY_SYSTEM_PROMPT.content
      .replace(/Você é a Kemily/g, `Você é a ${agentName}`)
      .replace('INFORMAÇÕES PESSOAIS:', `${knowledgeText}${observationsContext}INFORMAÇÕES PESSOAIS:`)
  };

  const enrichedMessages = [dynamicSystemPrompt, ...messages];

  try {
    let chatResponse;
    let providerUsed = 'OpenRouter';

    try {
      chatResponse = await fetch('https://openrouter.ai/api/v1/chat/completions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
          'HTTP-Referer': 'https://camila.reifonas.cloud',
          'X-Title': 'Camila ChatGPT replica',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          model: process.env.OPENROUTER_MODEL || 'openai/gpt-4.1-nano',
          messages: enrichedMessages,
          stream: true,
          temperature
        })
      });
    } catch (e) {
      console.error('Erro na conexão com OpenRouter, tentando Groq...', e.message);
    }

    // Se o OpenRouter falhar ou retornar erro (ex: status != 200), caímos no fallback do Groq
    if ((!chatResponse || !chatResponse.ok) && process.env.GROQ_API_KEY) {
      console.log('[Smart Router] Usando Groq (llama-3.3-70b-versatile) como fallback de streaming de texto...');
      providerUsed = 'Groq';
      try {
        chatResponse = await fetch('https://api.groq.com/openai/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.GROQ_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.GROQ_MODEL || 'llama-3.3-70b-versatile',
            messages: enrichedMessages,
            stream: true,
            temperature
          })
        });
      } catch (groqErr) {
        console.error('[Smart Router] Erro crítico: Falha no fallback do Groq também:', groqErr.message);
      }
    }

    // Se o OpenRouter e Groq falharem ou retornarem erro, caímos no fallback do Google Gemini
    if ((!chatResponse || !chatResponse.ok) && process.env.GOOGLE_AI_API_KEY) {
      console.log('[Smart Router] Usando Google Gemini como fallback de streaming de texto...');
      providerUsed = 'Google Gemini';
      try {
        chatResponse = await fetch('https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.GOOGLE_AI_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: process.env.GOOGLE_AI_MODEL || 'gemini-2.5-flash',
            messages: enrichedMessages,
            stream: true,
            temperature
          })
        });
      } catch (geminiErr) {
        console.error('[Smart Router] Erro crítico: Falha no fallback do Google Gemini também:', geminiErr.message);
      }
    }

    // Se o OpenRouter, Groq e Google Gemini falharem ou retornarem erro, caímos no fallback do MiniMax M2.7
    if ((!chatResponse || !chatResponse.ok) && process.env.MINIMAX_API_KEY) {
      console.log('[Smart Router] Usando MiniMax-M2.7 como fallback de streaming de texto...');
      providerUsed = 'MiniMax M2.7';
      try {
        chatResponse = await fetch('https://api.minimaxi.chat/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: 'MiniMax-M2.7',
            messages: enrichedMessages,
            stream: true,
            temperature
          })
        });
      } catch (minimaxErr) {
        console.error('[Smart Router] Erro crítico: Falha no fallback do MiniMax M2.7 também:', minimaxErr.message);
      }
    }

    if (!chatResponse || !chatResponse.ok) {
      const errorText = chatResponse ? await chatResponse.text() : 'Sem conexão com os servidores de IA';
      console.error(`Erro na API de IA (${providerUsed}):`, errorText);
      return res.status(500).json({ error: `Erro ao processar requisição na API de IA (${providerUsed})` });
    }

    // Configurar headers para Server-Sent Events (SSE)
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    const reader = chatResponse.body.getReader();
    const decoder = new TextDecoder('utf-8');
    let buffer = '';

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      buffer += decoder.decode(value, { stream: true });
      const lines = buffer.split('\n');
      
      // Manter a última linha inacabada no buffer
      buffer = lines.pop();

      for (const line of lines) {
        const cleanLine = line.trim();
        if (cleanLine.startsWith('data: ')) {
          const dataContent = cleanLine.slice(6);
          if (dataContent === '[DONE]') {
            res.write('data: [DONE]\n\n');
            continue;
          }
          try {
            const parsed = JSON.parse(dataContent);
            const content = parsed.choices?.[0]?.delta?.content || '';
            if (content) {
              res.write(`data: ${JSON.stringify({ content })}\n\n`);
            }
          } catch (e) {
            // Ignorar linhas incompletas ou JSON inválido
          }
        }
      }
    }

    // Enviar qualquer conteúdo restante no buffer
    if (buffer) {
      const cleanLine = buffer.trim();
      if (cleanLine.startsWith('data: ')) {
        const dataContent = cleanLine.slice(6);
        if (dataContent !== '[DONE]') {
          try {
            const parsed = JSON.parse(dataContent);
            const content = parsed.choices?.[0]?.delta?.content || '';
            if (content) {
              res.write(`data: ${JSON.stringify({ content })}\n\n`);
            }
          } catch (e) {}
        }
      }
    }

    res.write('data: [DONE]\n\n');
    res.end();

  } catch (error) {
    console.error('Erro na rota de chat:', error);
    res.status(500).json({ error: 'Erro interno no servidor ao chamar a API de IA' });
  }
});

// Rota coringa para redirecionar para index.html
app.get('*', requireAuth, (req, res) => {
  res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`Servidor rodando na porta ${PORT}`);
  initDatabase();
});
