import { Router, Request, Response } from 'express' import type { Knex } from 'knex' import { ProtocolEngine } from './brain' import type { PluginConfigStore } from '../../backend/src/core/plugin-config' function uuid(): string { try { return (crypto as any).randomUUID() } catch { return `${Date.now()}-${Math.random().toString(36).slice(2)}` } } export function createSecretariaRoutes(db: Knex, config: PluginConfigStore): Router { const router = Router() const brain = new ProtocolEngine(db, config) // ── Agents ─────────────────────────────────────────────────────────────── router.get('/agents', async (_req: Request, res: Response) => { try { const agents = await db('sec_agents').orderBy('created_at') res.json(agents) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.post('/agents', async (req: Request, res: Response) => { try { const { name, description, model, provider, temperature, context_window } = req.body const [agent] = await db('sec_agents').insert({ id: uuid(), name, description, model: model ?? 'gpt-4o-mini', provider: provider ?? 'openai', temperature: temperature ?? 0.7, context_window: context_window ?? 8, active: true, }).returning('*') res.status(201).json(agent) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.put('/agents/:id', async (req: Request, res: Response) => { try { const { id } = req.params const { name, description, model, provider, temperature, context_window, active } = req.body const [agent] = await db('sec_agents').where({ id }) .update({ name, description, model, provider, temperature, context_window, active, updated_at: new Date() }) .returning('*') res.json(agent) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.delete('/agents/:id', async (req: Request, res: Response) => { try { await db('sec_agents').where({ id: req.params.id }).delete() res.json({ ok: true }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // ── Brain Nodes ─────────────────────────────────────────────────────────── router.get('/agents/:agentId/nodes', async (req: Request, res: Response) => { try { const nodes = await db('sec_brain_nodes') .where({ agent_id: req.params.agentId }) .orderBy('sort_order') res.json(nodes) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.post('/agents/:agentId/nodes', async (req: Request, res: Response) => { try { const { type, title, content, sort_order } = req.body const [node] = await db('sec_brain_nodes').insert({ id: uuid(), agent_id: req.params.agentId, type, title, content, active: true, sort_order: sort_order ?? 99, }).returning('*') res.status(201).json(node) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.put('/nodes/:id', async (req: Request, res: Response) => { try { const { title, content, active, sort_order } = req.body const [node] = await db('sec_brain_nodes').where({ id: req.params.id }) .update({ title, content, active, sort_order, updated_at: new Date() }) .returning('*') res.json(node) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.delete('/nodes/:id', async (req: Request, res: Response) => { try { await db('sec_brain_nodes').where({ id: req.params.id }).delete() res.json({ ok: true }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // ── Conversations ──────────────────────────────────────────────────────── router.get('/conversations', async (req: Request, res: Response) => { try { const { agent_id } = req.query let q = db('sec_conversations').orderBy('updated_at', 'desc') if (agent_id) q = q.where({ agent_id: agent_id as string }) const convs = await q res.json(convs) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.post('/conversations', async (req: Request, res: Response) => { try { const { agent_id, contact_name } = req.body const [conv] = await db('sec_conversations').insert({ id: uuid(), agent_id, contact_name: contact_name ?? 'Usuário Teste', protocol_number: ProtocolEngine.generateProtocolNumber(), status: 'active', }).returning('*') res.status(201).json(conv) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.patch('/conversations/:id', async (req: Request, res: Response) => { try { const { status, contact_name } = req.body const [conv] = await db('sec_conversations').where({ id: req.params.id }) .update({ status, contact_name, updated_at: new Date() }).returning('*') res.json(conv) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.delete('/conversations/:id', async (req: Request, res: Response) => { try { await db('sec_conversations').where({ id: req.params.id }).delete() res.json({ ok: true }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // ── Messages ────────────────────────────────────────────────────────────── router.get('/conversations/:id/messages', async (req: Request, res: Response) => { try { const messages = await db('sec_messages') .where({ conversation_id: req.params.id }) .orderBy('created_at') res.json(messages) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // Enviar mensagem → aciona o cérebro → retorna resposta da IA router.post('/conversations/:id/chat', async (req: Request, res: Response) => { try { const { message } = req.body if (!message?.trim()) return res.status(400).json({ error: 'message is required' }) const reply = await brain.chat(String(req.params.id), message.trim()) res.json({ reply }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // Finalizar protocolo → gera resumo completo → apaga mensagens → fecha conversa router.post('/conversations/:id/finalize', async (req: Request, res: Response) => { try { const result = await brain.finalizeProtocol(String(req.params.id)) res.json(result) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // ── Calendar ────────────────────────────────────────────────────────────── router.get('/calendar', async (req: Request, res: Response) => { try { const qs = (k: string) => { const v = req.query[k]; return v ? String(v) : undefined } const from = qs('from'), to = qs('to'), status = qs('status') let q = db('sec_calendar').orderBy('date').orderBy('time_start') if (from) q = q.where('date', '>=', from) if (to) q = q.where('date', '<=', to) if (status) q = q.where({ status }) res.json(await q) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.post('/calendar', async (req: Request, res: Response) => { try { const { title, date, time_start, time_end, attendee_name, attendee_phone, notes } = req.body const [slot] = await db('sec_calendar').insert({ id: uuid(), title, date, time_start, time_end, attendee_name, attendee_phone, notes, status: 'available', }).returning('*') res.status(201).json(slot) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.put('/calendar/:id', async (req: Request, res: Response) => { try { const { title, date, time_start, time_end, attendee_name, attendee_phone, status, notes } = req.body const [slot] = await db('sec_calendar').where({ id: req.params.id }) .update({ title, date, time_start, time_end, attendee_name, attendee_phone, status, notes, updated_at: new Date() }) .returning('*') res.json(slot) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.delete('/calendar/:id', async (req: Request, res: Response) => { try { await db('sec_calendar').where({ id: req.params.id }).delete() res.json({ ok: true }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) // ── Numbers ─────────────────────────────────────────────────────────────── router.get('/numbers', async (_req: Request, res: Response) => { try { const numbers = await db('sec_numbers').orderBy('priority').orderBy('label') res.json(numbers) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.post('/numbers', async (req: Request, res: Response) => { try { const { instance_id, label, role, area, priority, notes } = req.body if (!instance_id) return res.status(400).json({ error: 'instance_id é obrigatório' }) // Upsert: se já existe um registro para esse instance_id, atualiza o role const existing = await db('sec_numbers').where({ instance_id }).first() if (existing) { const [num] = await db('sec_numbers').where({ instance_id }) .update({ label, role: role ?? 'clinic', area: area ?? null, priority: priority ?? 10, notes: notes ?? null, updated_at: new Date() }) .returning('*') return res.json(num) } const [num] = await db('sec_numbers').insert({ id: uuid(), instance_id, label, role: role ?? 'clinic', area: area ?? null, priority: priority ?? 10, active: true, notes: notes ?? null, }).returning('*') res.status(201).json(num) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.put('/numbers/:id', async (req: Request, res: Response) => { try { const { label, role, area, instance_id, priority, active, notes } = req.body const [num] = await db('sec_numbers').where({ id: req.params.id }) .update({ label, role, area, instance_id, priority, active, notes, updated_at: new Date() }) .returning('*') res.json(num) } catch (err: any) { res.status(500).json({ error: err.message }) } }) router.delete('/numbers/:id', async (req: Request, res: Response) => { try { await db('sec_numbers').where({ id: req.params.id }).delete() res.json({ ok: true }) } catch (err: any) { res.status(500).json({ error: err.message }) } }) return router }