'use strict';

// IMP-UX-01: GET /api/bills/deleted lists soft-deleted bills still inside the
// 30-day recovery window (newest first, with days_left), so the "Recently
// deleted" view can offer a restore beyond the transient undo toast. Bills
// purged past the window (or another user's) must not appear.
const test = require('node:test');
const assert = require('node:assert/strict');
const os = require('node:os');
const path = require('node:path');
const fs = require('node:fs');

const dbPath = path.join(os.tmpdir(), `bill-tracker-bills-deleted-route-${process.pid}.sqlite`);
process.env.DB_PATH = dbPath;

const { getDb, closeDb } = require('../db/database');

function createUser(db, suffix) {
  return db.prepare(
    "INSERT INTO users (username, password_hash, role, active) VALUES (?, 'x', 'user', 1)",
  ).run(`deleted-bills-${suffix}`).lastInsertRowid;
}

// daysAgo === null → an active (not deleted) bill; otherwise soft-deleted N days ago.
function insertBill(db, userId, name, daysAgo) {
  const id = db.prepare(
    'INSERT INTO bills (user_id, name, due_day, expected_amount, active) VALUES (?, ?, 1, 1000, ?)',
  ).run(userId, name, daysAgo == null ? 1 : 0).lastInsertRowid;
  if (daysAgo != null) {
    db.prepare("UPDATE bills SET deleted_at = datetime('now', ?) WHERE id = ?").run(`-${daysAgo} days`, id);
  }
  return id;
}

function callGetDeleted(userId) {
  const router = require('../routes/bills');
  const layer = router.stack.find(item => item.route?.path === '/deleted' && item.route.methods.get);
  assert.ok(layer, 'GET /deleted route should exist');
  const handler = layer.route.stack[0].handle;
  return new Promise((resolve, reject) => {
    const req = { query: {}, params: {}, user: { id: userId, role: 'user' } };
    const res = {
      statusCode: 200,
      status(code) { this.statusCode = code; return this; },
      json(data) { resolve({ status: this.statusCode, data }); },
    };
    try { handler(req, res); } catch (err) { reject(err); }
  });
}

test.after(() => {
  closeDb();
  for (const suffix of ['', '-wal', '-shm']) {
    try { fs.unlinkSync(dbPath + suffix); } catch {}
  }
});

test('GET /bills/deleted returns only recoverable soft-deleted bills, newest first', async () => {
  const db = getDb();
  const userId = createUser(db, 'a');

  insertBill(db, userId, 'Active Bill', null);       // not deleted
  insertBill(db, userId, 'Recently Deleted', 2);     // 2 days ago
  insertBill(db, userId, 'Older Deleted', 20);       // 20 days ago
  insertBill(db, userId, 'Purgeable', 40);           // past the 30-day window

  const { status, data } = await callGetDeleted(userId);
  assert.equal(status, 200);

  const names = data.map(b => b.name);
  assert.deepEqual(names, ['Recently Deleted', 'Older Deleted'], 'only in-window bills, newest first');
  assert.ok(!names.includes('Active Bill'), 'active bill excluded');
  assert.ok(!names.includes('Purgeable'), 'past-window bill excluded');

  const recentRow = data.find(b => b.name === 'Recently Deleted');
  assert.ok(recentRow.days_left >= 27 && recentRow.days_left <= 28, `~28 days left, got ${recentRow.days_left}`);
  assert.ok(recentRow.deleted_at, 'exposes deleted_at');
  assert.equal(recentRow.expected_amount, 10, 'money serialized to dollars (cents/100)');
});

test('GET /bills/deleted isolates by user', async () => {
  const db = getDb();
  const me = createUser(db, 'me');
  const other = createUser(db, 'other');
  insertBill(db, me, 'Mine', 1);
  insertBill(db, other, 'Theirs', 1);

  const { data } = await callGetDeleted(me);
  const names = data.map(b => b.name);
  assert.ok(names.includes('Mine'));
  assert.ok(!names.includes('Theirs'), "never leaks another user's deleted bills");
});
