/**
 * SQLite 封装：使用 sql.js（纯 JS/WebAssembly，无需 node-gyp）
 * 对外接口与 better-sqlite3 兼容：db.prepare().get/all/run、db.exec
 */
const path = require('path')
const fs = require('fs')

const dbPath = process.env.DB_PATH || path.join(__dirname, 'data.db')
let _db = null
/** sql.js 构造函数，initDb 时注入，供 reloadFromFile 使用 */
let _sqlJs = null

function getDb() {
  if (!_db) throw new Error('DB not initialized. Call initDb() first.')
  return _db
}

function wrapDatabase(nativeDb, persist) {
  return {
    prepare(sql) {
      return {
        get(...args) {
          const stmt = nativeDb.prepare(sql)
          stmt.bind(args.length ? args : null)
          const row = stmt.step() ? stmt.getAsObject() : undefined
          stmt.free()
          return row
        },
        all(...args) {
          const stmt = nativeDb.prepare(sql)
          stmt.bind(args.length ? args : null)
          const rows = []
          while (stmt.step()) rows.push(stmt.getAsObject())
          stmt.free()
          return rows
        },
        run(...args) {
          const stmt = nativeDb.prepare(sql)
          stmt.bind(args.length ? args : null)
          while (stmt.step());
          stmt.free()
          persist()
        },
      }
    },
    exec(sql) {
      const statements = sql.split(';').map((s) => s.trim()).filter(Boolean)
      statements.forEach((s) => nativeDb.run(s))
      persist()
    },
    pragma(str) {
      nativeDb.run('PRAGMA ' + str)
    },
  }
}

function runMigrations(db) {
  const exec = (sql) => db.exec(sql)
  const prepare = (sql) => db.prepare(sql)

  exec(`
  CREATE TABLE IF NOT EXISTS situation (
    id INTEGER PRIMARY KEY CHECK (id = 1),
    data TEXT NOT NULL,
    updated_at TEXT NOT NULL DEFAULT (datetime('now'))
  );
  CREATE TABLE IF NOT EXISTS force_summary (
    side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
    total_assets INTEGER NOT NULL,
    personnel INTEGER NOT NULL,
    naval_ships INTEGER NOT NULL,
    aircraft INTEGER NOT NULL,
    ground_units INTEGER NOT NULL,
    uav INTEGER NOT NULL,
    missile_consumed INTEGER NOT NULL,
    missile_stock INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS power_index (
    side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
    overall INTEGER NOT NULL,
    military_strength INTEGER NOT NULL,
    economic_power INTEGER NOT NULL,
    geopolitical_influence INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS force_asset (
    id TEXT PRIMARY KEY,
    side TEXT NOT NULL CHECK (side IN ('us', 'iran')),
    name TEXT NOT NULL,
    type TEXT NOT NULL,
    count INTEGER NOT NULL,
    status TEXT NOT NULL CHECK (status IN ('active', 'standby', 'alert')),
    lat REAL,
    lng REAL
  );
  CREATE TABLE IF NOT EXISTS key_location (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    side TEXT NOT NULL CHECK (side IN ('us', 'iran')),
    name TEXT NOT NULL,
    lat REAL NOT NULL,
    lng REAL NOT NULL,
    type TEXT,
    region TEXT
  );
  CREATE TABLE IF NOT EXISTS combat_losses (
    side TEXT PRIMARY KEY CHECK (side IN ('us', 'iran')),
    bases_destroyed INTEGER NOT NULL,
    bases_damaged INTEGER NOT NULL,
    personnel_killed INTEGER NOT NULL,
    personnel_wounded INTEGER NOT NULL,
    aircraft INTEGER NOT NULL,
    warships INTEGER NOT NULL,
    armor INTEGER NOT NULL,
    vehicles INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS wall_street_trend (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    time TEXT NOT NULL,
    value INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS retaliation_current (
    id INTEGER PRIMARY KEY CHECK (id = 1),
    value INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS retaliation_history (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    time TEXT NOT NULL,
    value INTEGER NOT NULL
  );
  CREATE TABLE IF NOT EXISTS situation_update (
    id TEXT PRIMARY KEY,
    timestamp TEXT NOT NULL,
    category TEXT NOT NULL,
    summary TEXT NOT NULL,
    severity TEXT NOT NULL
  );
  CREATE TABLE IF NOT EXISTS gdelt_events (
    event_id TEXT PRIMARY KEY,
    event_time TEXT NOT NULL,
    title TEXT NOT NULL,
    lat REAL NOT NULL,
    lng REAL NOT NULL,
    impact_score INTEGER NOT NULL,
    url TEXT,
    created_at TEXT DEFAULT (datetime('now'))
  );
  CREATE TABLE IF NOT EXISTS conflict_stats (
    id INTEGER PRIMARY KEY CHECK (id = 1),
    total_events INTEGER NOT NULL DEFAULT 0,
    high_impact_events INTEGER NOT NULL DEFAULT 0,
    estimated_casualties INTEGER NOT NULL DEFAULT 0,
    estimated_strike_count INTEGER NOT NULL DEFAULT 0,
    updated_at TEXT NOT NULL
  );
  CREATE TABLE IF NOT EXISTS news_content (
    id TEXT PRIMARY KEY,
    content_hash TEXT NOT NULL UNIQUE,
    title TEXT NOT NULL,
    summary TEXT NOT NULL,
    url TEXT NOT NULL DEFAULT '',
    source TEXT NOT NULL DEFAULT '',
    published_at TEXT NOT NULL,
    category TEXT NOT NULL DEFAULT 'other',
    severity TEXT NOT NULL DEFAULT 'medium',
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
  );
  `)
  try { exec('CREATE INDEX IF NOT EXISTS idx_news_content_hash ON news_content(content_hash)') } catch (_) {}
  try { exec('CREATE INDEX IF NOT EXISTS idx_news_content_published ON news_content(published_at DESC)') } catch (_) {}

  try {
    const cols = prepare('PRAGMA table_info(key_location)').all()
    const names = cols.map((c) => c.name)
    if (!names.includes('type')) exec('ALTER TABLE key_location ADD COLUMN type TEXT')
    if (!names.includes('region')) exec('ALTER TABLE key_location ADD COLUMN region TEXT')
    if (!names.includes('status')) exec('ALTER TABLE key_location ADD COLUMN status TEXT DEFAULT "operational"')
    if (!names.includes('damage_level')) exec('ALTER TABLE key_location ADD COLUMN damage_level INTEGER')
    if (!names.includes('attacked_at')) exec('ALTER TABLE key_location ADD COLUMN attacked_at TEXT')
  } catch (_) {}
  try {
    const lossCols = prepare('PRAGMA table_info(combat_losses)').all()
    const lossNames = lossCols.map((c) => c.name)
    if (!lossNames.includes('civilian_killed')) exec('ALTER TABLE combat_losses ADD COLUMN civilian_killed INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('civilian_wounded')) exec('ALTER TABLE combat_losses ADD COLUMN civilian_wounded INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('updated_at')) exec('ALTER TABLE combat_losses ADD COLUMN updated_at TEXT DEFAULT (datetime("now"))')
    if (!lossNames.includes('drones')) exec('ALTER TABLE combat_losses ADD COLUMN drones INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('missiles')) exec('ALTER TABLE combat_losses ADD COLUMN missiles INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('helicopters')) exec('ALTER TABLE combat_losses ADD COLUMN helicopters INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('submarines')) exec('ALTER TABLE combat_losses ADD COLUMN submarines INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('tanks')) exec('ALTER TABLE combat_losses ADD COLUMN tanks INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('carriers')) {
      exec('ALTER TABLE combat_losses ADD COLUMN carriers INTEGER NOT NULL DEFAULT 0')
      exec('UPDATE combat_losses SET carriers = tanks')
    }
    if (!lossNames.includes('civilian_ships')) exec('ALTER TABLE combat_losses ADD COLUMN civilian_ships INTEGER NOT NULL DEFAULT 0')
    if (!lossNames.includes('airport_port')) exec('ALTER TABLE combat_losses ADD COLUMN airport_port INTEGER NOT NULL DEFAULT 0')
  } catch (_) {}

  const addUpdatedAt = (table) => {
    try {
      const cols = prepare(`PRAGMA table_info(${table})`).all()
      if (!cols.some((c) => c.name === 'updated_at')) {
        exec(`ALTER TABLE ${table} ADD COLUMN updated_at TEXT DEFAULT (datetime("now"))`)
      }
    } catch (_) {}
  }
  ;['force_summary', 'power_index', 'force_asset', 'key_location', 'retaliation_current'].forEach(addUpdatedAt)

  try {
    exec(`
    CREATE TABLE IF NOT EXISTS visits (
      ip TEXT PRIMARY KEY,
      last_seen TEXT NOT NULL DEFAULT (datetime('now'))
    );
    CREATE TABLE IF NOT EXISTS visitor_count (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      total INTEGER NOT NULL DEFAULT 0
    );
    INSERT OR IGNORE INTO visitor_count (id, total) VALUES (1, 0);
    `)
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS feedback (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      content TEXT NOT NULL,
      ip TEXT,
      created_at TEXT NOT NULL DEFAULT (datetime('now'))
    )
    `)
    const fbCols = prepare('PRAGMA table_info(feedback)').all()
    const fbNames = (fbCols || []).map((c) => c.name)
    if (!fbNames.includes('created_at')) {
      exec('ALTER TABLE feedback ADD COLUMN created_at TEXT NOT NULL DEFAULT (datetime(\'now\'))')
    }
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS share_count (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      total INTEGER NOT NULL DEFAULT 0
    );
    INSERT OR IGNORE INTO share_count (id, total) VALUES (1, 0);
    `)
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS like_count (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      total INTEGER NOT NULL DEFAULT 0
    );
    INSERT OR IGNORE INTO like_count (id, total) VALUES (1, 0);
    `)
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS display_stats (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      viewers INTEGER NULL,
      cumulative INTEGER NULL,
      share_count INTEGER NULL,
      like_count INTEGER NULL,
      feedback_count INTEGER NULL
    );
    INSERT OR IGNORE INTO display_stats (id) VALUES (1);
    `)
  } catch (_) {}
  try {
    const dsCols = prepare('PRAGMA table_info(display_stats)').all()
    const dsNames = dsCols.map((c) => c.name)
    if (!dsNames.includes('override_enabled')) {
      exec('ALTER TABLE display_stats ADD COLUMN override_enabled INTEGER NOT NULL DEFAULT 0')
    }
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS map_strike_source (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      lng REAL NOT NULL,
      lat REAL NOT NULL
    );
    CREATE TABLE IF NOT EXISTS map_strike_line (
      source_id TEXT NOT NULL,
      target_lng REAL NOT NULL,
      target_lat REAL NOT NULL,
      target_name TEXT,
      struck_at TEXT,
      FOREIGN KEY (source_id) REFERENCES map_strike_source(id)
    );
    CREATE INDEX IF NOT EXISTS idx_map_strike_line_source ON map_strike_line(source_id);
    `)
  } catch (_) {}
  try {
    const lineCols = prepare('PRAGMA table_info(map_strike_line)').all()
    if (!lineCols.some((c) => c.name === 'struck_at')) {
      exec('ALTER TABLE map_strike_line ADD COLUMN struck_at TEXT')
    }
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS animation_config (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      strike_cutoff_days INTEGER NOT NULL DEFAULT 5,
      updated_at TEXT NOT NULL DEFAULT (datetime('now'))
    );
    INSERT OR IGNORE INTO animation_config (id, strike_cutoff_days) VALUES (1, 5);
    `)
  } catch (_) {}
  try {
    exec(`
    CREATE TABLE IF NOT EXISTS war_map_config (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      config TEXT NOT NULL,
      updated_at TEXT NOT NULL DEFAULT (datetime('now'))
    );
    CREATE TABLE IF NOT EXISTS crawler_config (
      id INTEGER PRIMARY KEY CHECK (id = 1),
      rss_interval_sec INTEGER NOT NULL DEFAULT 60
    );
    INSERT OR IGNORE INTO crawler_config (id, rss_interval_sec) VALUES (1, 60);
    `)
  } catch (_) {}

  // 生产环境可能未跑 seed 或使用旧 seed：补齐 war_map_config 与以色列→黎巴嫩打击线，保证攻击动画显示
  try {
    let row = db.prepare('SELECT 1 FROM war_map_config WHERE id = 1').get()
    if (!row) {
      const warMapConfig = {
        pincerAxes: [
          { start: [43.6, 37.2], end: [46.27, 38.08], name: 'North Pincer (Tabriz)' },
          { start: [45.0, 35.4], end: [46.99, 35.31], name: 'Central Pincer (Sanandaj)' },
          { start: [45.6, 35.2], end: [47.07, 34.31], name: 'South Pincer (Kermanshah)' },
        ],
        israelLebanonAxis: { start: [35.25, 32.95], end: [35.55, 33.45], name: 'Israel → Lebanon' },
        defenseLinePath: [[46.27, 38.08], [46.99, 35.31], [47.07, 34.31]],
      }
      db.prepare(
        'INSERT INTO war_map_config (id, config, updated_at) VALUES (1, ?, datetime(\'now\'))'
      ).run(JSON.stringify(warMapConfig))
    }

    // 确保 map_strike_source 有 israel（否则打击线无法关联）
    db.prepare(
      'INSERT OR IGNORE INTO map_strike_source (id, name, lng, lat) VALUES (\'israel\', \'以色列\', 34.78, 32.08)'
    ).run()

    const israelLebanonTargets = [
      [35.5, 33.86, '贝鲁特南郊指挥所', '2026-02-28T14:00:00.000Z'],
      [35.32, 33.34, '利塔尼弹药库', '2026-02-28T14:10:00.000Z'],
      [36.2, 34.01, '巴勒贝克后勤枢纽', '2026-02-28T14:20:00.000Z'],
      [35.19, 33.27, '提尔海岸阵地', '2026-02-28T14:30:00.000Z'],
      [36.38, 34.39, '赫尔梅勒无人机阵地', '2026-02-28T14:40:00.000Z'],
    ]
    const insertStrikeLine = db.prepare(
      'INSERT INTO map_strike_line (source_id, target_lng, target_lat, target_name, struck_at) VALUES (?, ?, ?, ?, ?)'
    )
    const hasStrikeLine = db.prepare(
      'SELECT 1 FROM map_strike_line WHERE source_id = ? AND target_lng = ? AND target_lat = ?'
    )
    for (const [lng, lat, name, struckAt] of israelLebanonTargets) {
      if (!hasStrikeLine.get('israel', lng, lat)) {
        insertStrikeLine.run('israel', lng, lat, name, struckAt)
      }
    }
  } catch (_) {}
}

async function initDb() {
  const initSqlJs = require('sql.js')
  const SQL = await initSqlJs()
  _sqlJs = SQL
  let data = new Uint8Array(0)
  if (fs.existsSync(dbPath)) {
    data = new Uint8Array(fs.readFileSync(dbPath))
  }
  const nativeDb = new SQL.Database(data)

  function persist() {
    try {
      const buf = nativeDb.export()
      fs.writeFileSync(dbPath, Buffer.from(buf))
    } catch (e) {
      console.error('[db] persist error:', e.message)
    }
  }

  nativeDb.run('PRAGMA journal_mode = WAL')
  const wrapped = wrapDatabase(nativeDb, persist)
  runMigrations(wrapped)
  _db = wrapped
  return _db
}

/**
 * 从磁盘重新加载 DB（爬虫写入同一文件后调用，使 Node 内存中的库与文件一致）
 */
function reloadFromFile() {
  if (!_sqlJs || !_db) throw new Error('DB not initialized. Call initDb() first.')
  let data = new Uint8Array(0)
  if (fs.existsSync(dbPath)) {
    data = new Uint8Array(fs.readFileSync(dbPath))
  }
  const nativeDb = new _sqlJs.Database(data)
  function persist() {
    try {
      const buf = nativeDb.export()
      fs.writeFileSync(dbPath, Buffer.from(buf))
    } catch (e) {
      console.error('[db] persist error:', e.message)
    }
  }
  nativeDb.run('PRAGMA journal_mode = WAL')
  const wrapped = wrapDatabase(nativeDb, persist)
  runMigrations(wrapped)
  _db = wrapped
}

const proxy = {
  prepare(sql) {
    return getDb().prepare(sql)
  },
  exec(sql) {
    return getDb().exec(sql)
  },
  pragma(str) {
    getDb().pragma(str)
  },
}

module.exports = proxy
module.exports.initDb = initDb
module.exports.getDb = getDb
module.exports.reloadFromFile = reloadFromFile
