const express = require('express'); const router = express.Router(); const { getDatabase } = require('../db/init'); // GET all heat cycles for a dog router.get('/heat-cycles/dog/:dogId', (req, res) => { try { const db = getDatabase(); const cycles = db.prepare(` SELECT * FROM heat_cycles WHERE dog_id = ? ORDER BY start_date DESC `).all(req.params.dogId); res.json(cycles); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET all active heat cycles (with dog info) router.get('/heat-cycles/active', (req, res) => { try { const db = getDatabase(); const cycles = db.prepare(` SELECT hc.*, d.name as dog_name, d.registration_number, d.breed, d.birth_date FROM heat_cycles hc JOIN dogs d ON hc.dog_id = d.id WHERE hc.end_date IS NULL OR hc.end_date >= date('now', '-30 days') ORDER BY hc.start_date DESC `).all(); res.json(cycles); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET all heat cycles (all dogs, for calendar population) router.get('/heat-cycles', (req, res) => { try { const db = getDatabase(); const { year, month } = req.query; let query = ` SELECT hc.*, d.name as dog_name, d.registration_number, d.breed FROM heat_cycles hc JOIN dogs d ON hc.dog_id = d.id `; const params = []; if (year && month) { query += ` WHERE strftime('%Y', hc.start_date) = ? AND strftime('%m', hc.start_date) = ?`; params.push(year, month.toString().padStart(2, '0')); } query += ' ORDER BY hc.start_date DESC'; const cycles = db.prepare(query).all(...params); res.json(cycles); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET breeding date suggestions for a heat cycle // Returns optimal breeding window based on start_date (days 9-15 of cycle) router.get('/heat-cycles/:id/suggestions', (req, res) => { try { const db = getDatabase(); const cycle = db.prepare(` SELECT hc.*, d.name as dog_name FROM heat_cycles hc JOIN dogs d ON hc.dog_id = d.id WHERE hc.id = ? `).get(req.params.id); if (!cycle) return res.status(404).json({ error: 'Heat cycle not found' }); const start = new Date(cycle.start_date); const addDays = (d, n) => { const r = new Date(d); r.setDate(r.getDate() + n); return r.toISOString().split('T')[0]; }; // Standard canine heat cycle windows res.json({ cycle_id: cycle.id, dog_name: cycle.dog_name, start_date: cycle.start_date, windows: [ { label: 'Proestrus', description: 'Bleeding begins, not yet receptive', start: addDays(start, 0), end: addDays(start, 8), color: 'pink', type: 'proestrus' }, { label: 'Optimal Breeding Window', description: 'Estrus — highest fertility, best time to breed', start: addDays(start, 9), end: addDays(start, 15), color: 'green', type: 'optimal' }, { label: 'Late Estrus', description: 'Fertility declining but breeding still possible', start: addDays(start, 16), end: addDays(start, 21), color: 'yellow', type: 'late' }, { label: 'Diestrus', description: 'Cycle ending, not receptive', start: addDays(start, 22), end: addDays(start, 28), color: 'gray', type: 'diestrus' } ], // If a breeding_date was logged, compute whelping estimate whelping: cycle.breeding_date ? { breeding_date: cycle.breeding_date, earliest: addDays(new Date(cycle.breeding_date), 58), expected: addDays(new Date(cycle.breeding_date), 63), latest: addDays(new Date(cycle.breeding_date), 68) } : null }); } catch (error) { res.status(500).json({ error: error.message }); } }); // POST create heat cycle router.post('/heat-cycles', (req, res) => { try { const { dog_id, start_date, end_date, breeding_date, breeding_successful, notes } = req.body; if (!dog_id || !start_date) { return res.status(400).json({ error: 'Dog ID and start date are required' }); } const db = getDatabase(); // Verify dog is female const dog = db.prepare('SELECT sex FROM dogs WHERE id = ?').get(dog_id); if (!dog || dog.sex !== 'female') { return res.status(400).json({ error: 'Dog must be female' }); } const result = db.prepare(` INSERT INTO heat_cycles (dog_id, start_date, end_date, breeding_date, breeding_successful, notes) VALUES (?, ?, ?, ?, ?, ?) `).run(dog_id, start_date, end_date || null, breeding_date || null, breeding_successful || 0, notes || null); const cycle = db.prepare('SELECT * FROM heat_cycles WHERE id = ?').get(result.lastInsertRowid); res.status(201).json(cycle); } catch (error) { res.status(500).json({ error: error.message }); } }); // PUT update heat cycle router.put('/heat-cycles/:id', (req, res) => { try { const { start_date, end_date, breeding_date, breeding_successful, notes } = req.body; const db = getDatabase(); db.prepare(` UPDATE heat_cycles SET start_date = ?, end_date = ?, breeding_date = ?, breeding_successful = ?, notes = ? WHERE id = ? `).run(start_date, end_date || null, breeding_date || null, breeding_successful || 0, notes || null, req.params.id); const cycle = db.prepare('SELECT * FROM heat_cycles WHERE id = ?').get(req.params.id); res.json(cycle); } catch (error) { res.status(500).json({ error: error.message }); } }); // DELETE heat cycle router.delete('/heat-cycles/:id', (req, res) => { try { const db = getDatabase(); db.prepare('DELETE FROM heat_cycles WHERE id = ?').run(req.params.id); res.json({ message: 'Heat cycle deleted successfully' }); } catch (error) { res.status(500).json({ error: error.message }); } }); // GET whelping calculator (standalone) router.get('/whelping-calculator', (req, res) => { try { const { breeding_date } = req.query; if (!breeding_date) { return res.status(400).json({ error: 'Breeding date is required' }); } const breedDate = new Date(breeding_date); const addDays = (d, n) => { const r = new Date(d); r.setDate(r.getDate() + n); return r.toISOString().split('T')[0]; }; res.json({ breeding_date, expected_whelping_date: addDays(breedDate, 63), earliest_date: addDays(breedDate, 58), latest_date: addDays(breedDate, 68), gestation_days: 63 }); } catch (error) { res.status(500).json({ error: error.message }); } }); module.exports = router;