const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); const dbPath = path.join(__dirname, '../../data'); const db = new Database(path.join(dbPath, 'breedr.db')); function getDatabase() { return db; } function initDatabase() { db.pragma('foreign_keys = ON'); db.pragma('journal_mode = WAL'); // ── Dogs ──────────────────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS dogs ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, registration_number TEXT, breed TEXT NOT NULL, sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), birth_date TEXT, color TEXT, microchip TEXT, litter_id INTEGER, is_active INTEGER DEFAULT 1, is_champion INTEGER DEFAULT 0, is_external INTEGER DEFAULT 0, chic_number TEXT, age_at_death TEXT, cause_of_death TEXT, photo_urls TEXT DEFAULT '[]', notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ) `); // migrate: add columns if missing (safe on existing DBs) const dogMigrations = [ ['is_champion', 'INTEGER DEFAULT 0'], ['is_external', 'INTEGER DEFAULT 0'], ['chic_number', 'TEXT'], ['age_at_death', 'TEXT'], ['cause_of_death', 'TEXT'], ]; for (const [col, def] of dogMigrations) { try { db.exec(`ALTER TABLE dogs ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ } } // ── Parents ────────────────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS parents ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, parent_id INTEGER NOT NULL, parent_type TEXT NOT NULL CHECK(parent_type IN ('sire', 'dam')), FOREIGN KEY (dog_id) REFERENCES dogs(id), FOREIGN KEY (parent_id) REFERENCES dogs(id) ) `); // ── Breeding Records ───────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS breeding_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, sire_id INTEGER NOT NULL, dam_id INTEGER NOT NULL, breeding_date TEXT, due_date TEXT, conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')), notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (sire_id) REFERENCES dogs(id), FOREIGN KEY (dam_id) REFERENCES dogs(id) ) `); // ── Litters ────────────────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS litters ( id INTEGER PRIMARY KEY AUTOINCREMENT, breeding_id INTEGER, sire_id INTEGER NOT NULL, dam_id INTEGER NOT NULL, whelp_date TEXT, total_count INTEGER DEFAULT 0, male_count INTEGER DEFAULT 0, female_count INTEGER DEFAULT 0, stillborn_count INTEGER DEFAULT 0, notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (breeding_id) REFERENCES breeding_records(id), FOREIGN KEY (sire_id) REFERENCES dogs(id), FOREIGN KEY (dam_id) REFERENCES dogs(id) ) `); // ── Health Records (OFA-extended) ───────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS health_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, record_type TEXT NOT NULL, test_type TEXT, test_name TEXT, test_date TEXT NOT NULL, ofa_result TEXT, ofa_number TEXT, performed_by TEXT, expires_at TEXT, document_url TEXT, result TEXT, vet_name TEXT, next_due TEXT, notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (dog_id) REFERENCES dogs(id) ) `); // migrate: add OFA-specific columns if missing (covers existing DBs) const healthMigrations = [ ['test_type', 'TEXT'], ['ofa_result', 'TEXT'], ['ofa_number', 'TEXT'], ['performed_by', 'TEXT'], ['expires_at', 'TEXT'], ['document_url', 'TEXT'], ['result', 'TEXT'], ['vet_name', 'TEXT'], ['next_due', 'TEXT'], ]; for (const [col, def] of healthMigrations) { try { db.exec(`ALTER TABLE health_records ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ } } // ── Genetic Tests (DNA Panel) ────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS genetic_tests ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, test_provider TEXT, marker TEXT NOT NULL, result TEXT NOT NULL CHECK(result IN ('clear', 'carrier', 'affected', 'not_tested')), test_date TEXT, document_url TEXT, notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (dog_id) REFERENCES dogs(id) ) `); const geneticMigrations = [ ['test_provider', 'TEXT'], ['marker', "TEXT NOT NULL DEFAULT 'unknown'"], ['result', "TEXT NOT NULL DEFAULT 'not_tested'"], ['test_date', 'TEXT'], ['document_url', 'TEXT'], ['notes', 'TEXT'] ]; for (const [col, def] of geneticMigrations) { try { db.exec(`ALTER TABLE genetic_tests ADD COLUMN ${col} ${def}`); } catch (_) {} } // ── Cancer History ──────────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS cancer_history ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, cancer_type TEXT, age_at_diagnosis TEXT, age_at_death TEXT, cause_of_death TEXT, notes TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')), FOREIGN KEY (dog_id) REFERENCES dogs(id) ) `); const cancerMigrations = [ ['cancer_type', 'TEXT'], ['age_at_diagnosis', 'TEXT'], ['age_at_death', 'TEXT'], ['cause_of_death', 'TEXT'], ['notes', 'TEXT'] ]; for (const [col, def] of cancerMigrations) { try { db.exec(`ALTER TABLE cancer_history ADD COLUMN ${col} ${def}`); } catch (_) {} } // ── Settings ────────────────────────────────────────────────────────────── db.exec(` CREATE TABLE IF NOT EXISTS settings ( id INTEGER PRIMARY KEY AUTOINCREMENT, kennel_name TEXT DEFAULT 'BREEDR', kennel_tagline TEXT, kennel_address TEXT, kennel_phone TEXT, kennel_email TEXT, kennel_website TEXT, kennel_akc_id TEXT, kennel_breed TEXT, owner_name TEXT, created_at TEXT DEFAULT (datetime('now')), updated_at TEXT DEFAULT (datetime('now')) ) `); const kennelCols = [ ['kennel_name', "TEXT DEFAULT 'BREEDR'"], ['kennel_tagline', 'TEXT'], ['kennel_address', 'TEXT'], ['kennel_phone', 'TEXT'], ['kennel_email', 'TEXT'], ['kennel_website', 'TEXT'], ['kennel_akc_id', 'TEXT'], ['kennel_breed', 'TEXT'], ['owner_name', 'TEXT'], ]; for (const [col, def] of kennelCols) { try { db.exec(`ALTER TABLE settings ADD COLUMN ${col} ${def}`); } catch (_) { /* already exists */ } } const existing = db.prepare('SELECT id FROM settings LIMIT 1').get(); if (!existing) { db.prepare(`INSERT INTO settings (kennel_name) VALUES (?)`).run('BREEDR'); } console.log('✓ Database initialized successfully'); } module.exports = { getDatabase, initDatabase };