// engine/db/migrations/007-watches.js
//
// Phase 6: move engine/watches.json into a `watches` table.
//
// watches.json is an array of watch jobs polled every 3 ticks. Most
// installs carry only a handful (operator-defined notifications, plan-
// completion gates, etc), but the table is still indexed by status +
// target_type so dashboards / future filters can do "show active PR
// watches" without pulling the whole array.
//
// Schema is the same hybrid pattern Phases 2/3/5 use: typed projection
// columns for the hot filters, a `data` TEXT blob for everything else.

const path = require('path');
const fs = require('fs');

function _resolveMinionsDir() {
  // Canonical order: MINIONS_TEST_DIR wins over MINIONS_HOME (matches
  // engine/db/index.js + migrations 002/003/004). Reversing this lets a
  // test that sets both env vars open the DB at MINIONS_TEST_DIR but
  // backfill its tables from MINIONS_HOME — cross-directory corruption.
  const envHome = process.env.MINIONS_TEST_DIR || process.env.MINIONS_HOME;
  if (envHome) return envHome;
  try { return require('../../shared').MINIONS_DIR; } catch { return null; }
}

function _toMs(v) {
  if (v == null) return null;
  if (typeof v === 'number') return Number.isFinite(v) ? v : null;
  const parsed = Date.parse(v);
  return Number.isFinite(parsed) ? parsed : null;
}

module.exports = {
  version: 7,
  description: 'watches: schema + watches.json backfill',
  up(db) {
    db.exec(`
      CREATE TABLE watches (
        id              TEXT PRIMARY KEY,
        target          TEXT,
        target_type     TEXT,
        condition       TEXT,
        status          TEXT NOT NULL,
        owner           TEXT,
        created_at      INTEGER,
        last_checked    INTEGER,
        last_triggered  INTEGER,
        data            TEXT NOT NULL,
        updated_at      INTEGER NOT NULL
      );
      CREATE INDEX idx_watches_status       ON watches(status);
      CREATE INDEX idx_watches_target_type  ON watches(target_type);
      CREATE INDEX idx_watches_target       ON watches(target);
    `);

    const minionsDir = _resolveMinionsDir();
    if (!minionsDir) return;
    const watchesPath = path.join(minionsDir, 'engine', 'watches.json');
    if (!fs.existsSync(watchesPath)) return;

    let raw;
    try { raw = JSON.parse(fs.readFileSync(watchesPath, 'utf8')); }
    catch (e) {
      throw new Error(`engine/db/007-watches: cannot parse watches.json: ${e.message}`);
    }
    if (!Array.isArray(raw)) return;

    const now = Date.now();
    const insert = db.prepare(`
      INSERT INTO watches (id, target, target_type, condition, status, owner, created_at, last_checked, last_triggered, data, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    `);

    let inserted = 0;
    for (const w of raw) {
      if (!w || typeof w !== 'object' || !w.id) continue;
      try {
        insert.run(
          String(w.id),
          typeof w.target === 'string' ? w.target : (w.target == null ? null : JSON.stringify(w.target)),
          w.targetType || null,
          w.condition || null,
          String(w.status || 'active'),
          w.owner || null,
          _toMs(w.created_at),
          _toMs(w.last_checked),
          _toMs(w.last_triggered),
          JSON.stringify(w),
          now,
        );
        inserted += 1;
      } catch { /* duplicate id — skip defensively */ }
    }

    // eslint-disable-next-line no-console
    console.log(`[db-migrate] v7: backfilled ${inserted} watches; watches.json kept as dual-write mirror`);
  },
};
