const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');

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

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

function createUser(db, suffix) {
  return db.prepare(`
    INSERT INTO users (username, password_hash, role, active, email, created_at, updated_at)
    VALUES (?, 'x', 'user', 1, ?, datetime('now'), datetime('now'))
  `).run(`reorder-user-${suffix}`, `reorder-user-${suffix}@local`).lastInsertRowid;
}

function createBill(db, userId, name, dueDay) {
  return db.prepare(`
    INSERT INTO bills (user_id, name, due_day, expected_amount)
    VALUES (?, ?, ?, 2500)
  `).run(userId, name, dueDay).lastInsertRowid;
}

function callBillsRoute(routePath, method, { userId, params = {}, query = {}, body = {} }) {
  const billsRouter = require('../routes/bills');
  const layer = billsRouter.stack.find(item => item.route?.path === routePath && item.route.methods[method]);
  assert.ok(layer, `route ${method.toUpperCase()} ${routePath} should exist`);
  const handler = layer.route.stack[0].handle;

  return new Promise((resolve, reject) => {
    const req = {
      body,
      params,
      query,
      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']) {
    fs.rmSync(`${dbPath}${suffix}`, { force: true });
  }
});

test('bill reorder endpoint persists tracker order for the current user', async () => {
  const db = getDb();
  const userId = createUser(db, 'owner');
  const water = createBill(db, userId, 'Water', 10);
  const power = createBill(db, userId, 'Power', 5);
  const rent = createBill(db, userId, 'Rent', 20);

  const initial = getTracker(userId, { year: 2026, month: 5 }, new Date('2026-05-01T12:00:00Z'));
  assert.deepEqual(initial.rows.map(row => row.id), [power, water, rent]);

  const response = await callBillsRoute('/reorder', 'put', {
    userId,
    body: {
      [rent]: 0,
      [water]: 1,
      [power]: 2,
    },
  });

  assert.equal(response.status, 200);
  assert.equal(response.data.success, true);

  const tracker = getTracker(userId, { year: 2026, month: 5 }, new Date('2026-05-01T12:00:00Z'));
  assert.deepEqual(tracker.rows.map(row => row.id), [rent, water, power]);
});

test('bill reorder rejects bills outside the current user scope', async () => {
  const db = getDb();
  const ownerId = createUser(db, 'scoped-owner');
  const otherId = createUser(db, 'scoped-other');
  const ownerBill = createBill(db, ownerId, 'Internet', 7);
  const otherBill = createBill(db, otherId, 'Other Internet', 7);

  const response = await callBillsRoute('/reorder', 'put', {
    userId: ownerId,
    body: {
      [ownerBill]: 0,
      [otherBill]: 1,
    },
  });

  assert.equal(response.status, 404);
});

test('bill archived endpoint toggles tracker visibility without deleting the bill', async () => {
  const db = getDb();
  const userId = createUser(db, 'archive');
  const billId = createBill(db, userId, 'Streaming', 12);

  const archived = await callBillsRoute('/:id/archived', 'put', {
    userId,
    params: { id: String(billId) },
    body: { archived: true },
  });

  assert.equal(archived.status, 200);
  assert.equal(archived.data.archived, true);
  assert.equal(getTracker(userId, { year: 2026, month: 5 }, new Date('2026-05-01T12:00:00Z')).rows.length, 0);

  const restored = await callBillsRoute('/:id/archived', 'put', {
    userId,
    params: { id: String(billId) },
    body: { archived: false },
  });

  assert.equal(restored.status, 200);
  assert.equal(restored.data.archived, false);
  assert.equal(getTracker(userId, { year: 2026, month: 5 }, new Date('2026-05-01T12:00:00Z')).rows.length, 1);
});
