/**
 * Database layer using the built-in node:sqlite module (Node.js 22.5+).
 * No native compilation required.
 */

const { DatabaseSync } = require('node:sqlite');
const bcrypt = require('bcryptjs');
const path   = require('path');
const fs     = require('fs');
const { v4: uuidv4 } = require('uuid');
const log    = require('./logger');

const DB_PATH = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'sms-gateway.db');

const dataDir = path.dirname(DB_PATH);
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true });

let db;

function getDB() {
  if (!db) {
    db = new DatabaseSync(DB_PATH);
    db.exec('PRAGMA journal_mode = WAL');
    db.exec('PRAGMA foreign_keys = ON');
  }
  return db;
}

function initDB() {
  const db = getDB();

  db.exec(`
    CREATE TABLE IF NOT EXISTS config (
      key   TEXT PRIMARY KEY,
      value TEXT NOT NULL
    );

    CREATE TABLE IF NOT EXISTS admins (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      username   TEXT NOT NULL UNIQUE,
      password   TEXT NOT NULL,
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS api_tokens (
      id         INTEGER PRIMARY KEY AUTOINCREMENT,
      name       TEXT NOT NULL,
      token      TEXT NOT NULL UNIQUE,
      active     INTEGER NOT NULL DEFAULT 1,
      created_at TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS webhooks (
      id           INTEGER PRIMARY KEY AUTOINCREMENT,
      name         TEXT NOT NULL,
      phone_number TEXT,
      url          TEXT NOT NULL,
      method       TEXT NOT NULL DEFAULT 'POST',
      headers      TEXT,
      events       TEXT NOT NULL DEFAULT 'received',
      active       INTEGER NOT NULL DEFAULT 1,
      created_at   TEXT DEFAULT (datetime('now'))
    );

    CREATE TABLE IF NOT EXISTS messages (
      id            INTEGER PRIMARY KEY AUTOINCREMENT,
      direction     TEXT NOT NULL,
      phone_number  TEXT NOT NULL,
      message       TEXT NOT NULL,
      status        TEXT NOT NULL DEFAULT 'pending',
      device_msg_id TEXT,
      created_at    TEXT DEFAULT (datetime('now'))
    );

    CREATE INDEX IF NOT EXISTS idx_messages_direction  ON messages(direction);
    CREATE INDEX IF NOT EXISTS idx_messages_phone      ON messages(phone_number);
    CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at);
    CREATE INDEX IF NOT EXISTS idx_messages_device_id  ON messages(device_msg_id);
  `);

  // Seed admin from environment if none exists
  const adminUser = process.env.ADMIN_USERNAME || 'admin';
  const adminPass = process.env.ADMIN_PASSWORD || 'changeme';
  const existing  = db.prepare('SELECT id FROM admins WHERE username = ?').get(adminUser);
  if (!existing) {
    const hash = bcrypt.hashSync(adminPass, 10);
    db.prepare('INSERT INTO admins (username, password) VALUES (?, ?)').run(adminUser, hash);
    log.info('DB', `Created admin user: ${adminUser}`);
  }

  // Seed device config defaults (only insert if key not yet present)
  const upsert = db.prepare('INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)');
  const defaults = {
    device_ip:       process.env.DEVICE_IP       || '',
    device_username: process.env.DEVICE_USERNAME || 'admin',
    device_password: process.env.DEVICE_PASSWORD || '',
    device_modem:    process.env.DEVICE_MODEM    || '1-1',
    poll_interval:   process.env.POLL_INTERVAL   || '30',
  };
  for (const [key, value] of Object.entries(defaults)) upsert.run(key, value);

  log.info('DB', 'Initialized:', DB_PATH);
}

// ── Config ────────────────────────────────────────────────────────────────────

function getConfig(key) {
  const row = getDB().prepare('SELECT value FROM config WHERE key = ?').get(key);
  return row ? row.value : null;
}

function setConfig(key, value) {
  getDB().prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)').run(key, String(value));
}

function getAllConfig() {
  return getDB().prepare('SELECT key, value FROM config').all()
    .reduce((acc, r) => { acc[r.key] = r.value; return acc; }, {});
}

// ── Admins ────────────────────────────────────────────────────────────────────

function getAdmin(username) {
  return getDB().prepare('SELECT * FROM admins WHERE username = ?').get(username) || null;
}

function updateAdminPassword(username, newPassword) {
  const hash = bcrypt.hashSync(newPassword, 10);
  getDB().prepare('UPDATE admins SET password = ? WHERE username = ?').run(hash, username);
}

// ── API Tokens ─────────────────────────────────────────────────────────────────

function createToken(name) {
  const db    = getDB();
  const token = uuidv4().replace(/-/g, '') + uuidv4().replace(/-/g, '');
  db.prepare('INSERT INTO api_tokens (name, token) VALUES (?, ?)').run(name, token);
  return db.prepare('SELECT * FROM api_tokens WHERE token = ?').get(token);
}

function listTokens() {
  return getDB().prepare(
    'SELECT id, name, active, created_at FROM api_tokens ORDER BY created_at DESC'
  ).all();
}

function getTokenByValue(token) {
  return getDB().prepare('SELECT * FROM api_tokens WHERE token = ? AND active = 1').get(token) || null;
}

function deleteToken(id) {
  getDB().prepare('DELETE FROM api_tokens WHERE id = ?').run(id);
}

function toggleToken(id, active) {
  getDB().prepare('UPDATE api_tokens SET active = ? WHERE id = ?').run(active ? 1 : 0, id);
}

// ── Webhooks ──────────────────────────────────────────────────────────────────

function listWebhooks() {
  return getDB().prepare('SELECT * FROM webhooks ORDER BY created_at DESC').all();
}

function createWebhook({ name, phone_number, url, method = 'POST', headers, events = 'received' }) {
  const db     = getDB();
  const result = db.prepare(
    'INSERT INTO webhooks (name, phone_number, url, method, headers, events) VALUES (?, ?, ?, ?, ?, ?)'
  ).run(name, phone_number || null, url, method, headers ? JSON.stringify(headers) : null, events);
  return db.prepare('SELECT * FROM webhooks WHERE id = ?').get(result.lastInsertRowid);
}

function updateWebhook(id, fields) {
  const db      = getDB();
  const allowed = ['name', 'phone_number', 'url', 'method', 'headers', 'events', 'active'];
  const sets    = [];
  const values  = [];
  for (const [k, v] of Object.entries(fields)) {
    if (allowed.includes(k)) {
      sets.push(`${k} = ?`);
      values.push(k === 'headers' && v && typeof v === 'object' ? JSON.stringify(v) : v);
    }
  }
  if (!sets.length) return db.prepare('SELECT * FROM webhooks WHERE id = ?').get(id);
  values.push(id);
  db.prepare(`UPDATE webhooks SET ${sets.join(', ')} WHERE id = ?`).run(...values);
  return db.prepare('SELECT * FROM webhooks WHERE id = ?').get(id);
}

function deleteWebhook(id) {
  getDB().prepare('DELETE FROM webhooks WHERE id = ?').run(id);
}

function getActiveWebhooksForEvent(event, phoneNumber) {
  return getDB().prepare(`
    SELECT * FROM webhooks
    WHERE active = 1
      AND (events = ? OR events = 'both')
      AND (phone_number IS NULL OR phone_number = ?)
  `).all(event, phoneNumber);
}

// ── Messages ──────────────────────────────────────────────────────────────────

function saveMessage({ direction, phone_number, message, status = 'received', device_msg_id }) {
  const db     = getDB();
  const result = db.prepare(
    'INSERT INTO messages (direction, phone_number, message, status, device_msg_id) VALUES (?, ?, ?, ?, ?)'
  ).run(direction, phone_number, message, status, device_msg_id || null);
  return db.prepare('SELECT * FROM messages WHERE id = ?').get(result.lastInsertRowid);
}

function updateMessageStatus(id, status) {
  getDB().prepare('UPDATE messages SET status = ? WHERE id = ?').run(status, id);
}

function listMessages({ direction, limit = 100, offset = 0, phone_number } = {}) {
  const conditions = [];
  const values     = [];
  if (direction)    { conditions.push('direction = ?');    values.push(direction); }
  if (phone_number) { conditions.push('phone_number = ?'); values.push(phone_number); }
  const where = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
  values.push(limit, offset);
  return getDB().prepare(
    `SELECT * FROM messages ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`
  ).all(...values);
}

function messageExistsByDeviceId(device_msg_id) {
  return !!getDB().prepare('SELECT id FROM messages WHERE device_msg_id = ?').get(String(device_msg_id));
}

function getMessageStats() {
  const db       = getDB();
  const sent     = db.prepare("SELECT COUNT(*) AS c FROM messages WHERE direction='sent'").get().c;
  const received = db.prepare("SELECT COUNT(*) AS c FROM messages WHERE direction='received'").get().c;
  const failed   = db.prepare("SELECT COUNT(*) AS c FROM messages WHERE status='failed'").get().c;
  return { sent, received, failed };
}

module.exports = {
  initDB, getDB, getConfig, setConfig, getAllConfig,
  getAdmin, updateAdminPassword,
  createToken, listTokens, getTokenByValue, deleteToken, toggleToken,
  listWebhooks, createWebhook, updateWebhook, deleteWebhook, getActiveWebhooksForEvent,
  saveMessage, updateMessageStatus, listMessages, messageExistsByDeviceId, getMessageStats,
};
