const express = require('express');
const router = express.Router();
const { getDb } = require('../db/database');
const { standardizeError } = require('../middleware/errorFormatter');
const { calculateSnowball, calculateAvalanche } = require('../services/snowballService');
const { calculateMinimumOnly, debtAprSnapshot } = require('../services/aprService');

const DEBT_LIKE_CLAUSES = `(
  b.snowball_include = 1
  OR (
    COALESCE(b.snowball_exempt, 0) = 0
    AND (
      LOWER(c.name) LIKE '%credit%'
      OR LOWER(c.name) LIKE '%loan%'
      OR LOWER(c.name) LIKE '%debt%'
    )
  )
)`;

function isRamseyMode(userId) {
  const db = getDb();
  const row = db.prepare(`
    SELECT value
    FROM user_settings
    WHERE user_id = ? AND key = 'snowball_ramsey_mode'
  `).get(userId);
  return row ? row.value !== 'false' && row.value !== '0' : true;
}

function getUserBoolSetting(userId, key, fallback = false) {
  const db = getDb();
  const row = db.prepare(`
    SELECT value
    FROM user_settings
    WHERE user_id = ? AND key = ?
  `).get(userId, key);
  if (!row) return fallback;
  return row.value === 'true' || row.value === '1';
}

function upsertUserSetting(db, userId, key, value) {
  db.prepare(`
    INSERT INTO user_settings (user_id, key, value, updated_at)
    VALUES (?, ?, ?, datetime('now'))
    ON CONFLICT(user_id, key) DO UPDATE SET
      value = excluded.value,
      updated_at = datetime('now')
  `).run(userId, key, String(value));
}

function getDebtQuery(ramseyMode) {
  const orderBy = ramseyMode
    ? `
    CASE WHEN b.current_balance IS NULL THEN 1 ELSE 0 END ASC,
    b.current_balance ASC,
    LOWER(b.name) ASC,
    b.id ASC`
    : `
    CASE WHEN b.snowball_order IS NULL THEN 1 ELSE 0 END ASC,
    b.snowball_order ASC,
    CASE WHEN b.current_balance IS NULL THEN 1 ELSE 0 END ASC,
    b.current_balance ASC`;

  return `
  SELECT b.*, c.name AS category_name
  FROM bills b
  LEFT JOIN categories c ON b.category_id = c.id AND c.user_id = b.user_id AND c.deleted_at IS NULL
  WHERE b.user_id = ?
    AND b.active = 1
    AND b.deleted_at IS NULL
    AND ${DEBT_LIKE_CLAUSES}
  ORDER BY${orderBy}
  `;
}

function getDebtBills(userId) {
  const db = getDb();
  return db.prepare(getDebtQuery(isRamseyMode(userId))).all(userId);
}

// GET /api/snowball — server-filtered debt bills, pre-sorted by snowball_order
router.get('/', (req, res) => {
  res.json(getDebtBills(req.user.id));
});

// GET /api/snowball/settings — extra monthly payment for this user
router.get('/settings', (req, res) => {
  const db = getDb();
  const user = db.prepare('SELECT snowball_extra_payment FROM users WHERE id = ?').get(req.user.id);
  res.json({
    extra_payment: user?.snowball_extra_payment ?? 0,
    ramsey_mode: isRamseyMode(req.user.id),
    ready_current_on_bills: getUserBoolSetting(req.user.id, 'snowball_ready_current_on_bills'),
    ready_emergency_fund: getUserBoolSetting(req.user.id, 'snowball_ready_emergency_fund'),
  });
});

// PATCH /api/snowball/settings — save extra monthly payment
router.patch('/settings', (req, res) => {
  const { extra_payment, ramsey_mode, ready_current_on_bills, ready_emergency_fund } = req.body;
  let val = 0;

  if (extra_payment !== undefined && extra_payment !== null && extra_payment !== '') {
    val = parseFloat(extra_payment);
    if (!Number.isFinite(val) || val < 0) {
      return res.status(400).json(standardizeError(
        'extra_payment must be a non-negative number',
        'VALIDATION_ERROR',
        'extra_payment',
      ));
    }
  }

  const db = getDb();
  const save = db.transaction(() => {
    if (extra_payment !== undefined) {
      db.prepare('UPDATE users SET snowball_extra_payment = ? WHERE id = ?').run(val, req.user.id);
    }

    if (ramsey_mode !== undefined) {
      upsertUserSetting(db, req.user.id, 'snowball_ramsey_mode', ramsey_mode ? 'true' : 'false');
    }

    if (ready_current_on_bills !== undefined) {
      upsertUserSetting(db, req.user.id, 'snowball_ready_current_on_bills', ready_current_on_bills ? 'true' : 'false');
    }

    if (ready_emergency_fund !== undefined) {
      upsertUserSetting(db, req.user.id, 'snowball_ready_emergency_fund', ready_emergency_fund ? 'true' : 'false');
    }
  });
  save();

  const user = db.prepare('SELECT snowball_extra_payment FROM users WHERE id = ?').get(req.user.id);
  res.json({
    extra_payment: user?.snowball_extra_payment ?? 0,
    ramsey_mode: isRamseyMode(req.user.id),
    ready_current_on_bills: getUserBoolSetting(req.user.id, 'snowball_ready_current_on_bills'),
    ready_emergency_fund: getUserBoolSetting(req.user.id, 'snowball_ready_emergency_fund'),
  });
});

// GET /api/snowball/projection — snowball, avalanche, minimum-only projections
// Each debt result is enriched with current APR metrics (monthly interest, etc.)
router.get('/projection', (req, res) => {
  const db = getDb();

  const bills = getDebtBills(req.user.id);
  const user  = db.prepare('SELECT snowball_extra_payment FROM users WHERE id = ?').get(req.user.id);
  const extra = user?.snowball_extra_payment ?? 0;

  // Build a lookup of APR snapshots keyed by bill id (computed once from current balances)
  const aprByBill = {};
  for (const b of bills) {
    const snap = debtAprSnapshot(b);
    if (snap) aprByBill[b.id] = snap;
  }

  // Enrich each debt result with its APR snapshot
  function enrich(projection) {
    return {
      ...projection,
      debts: projection.debts.map(d => ({
        ...d,
        apr_snapshot: aprByBill[d.id] ?? null,
      })),
    };
  }

  const now          = new Date();
  const snowball     = enrich(calculateSnowball(bills, extra, now));
  const avalanche    = enrich(calculateAvalanche(bills, extra, now));
  const minimum_only = enrich(calculateMinimumOnly(bills, now));

  // Comparison: what does the snowball save vs just paying minimums?
  const comparison = buildComparison(snowball, minimum_only);

  res.json({ snowball, avalanche, minimum_only, comparison });
});

// Build a summary comparing snowball to the minimum-only baseline
function buildComparison(snowball, minimum_only) {
  const sbMonths = snowball.months_to_freedom;
  const moMonths = minimum_only.months_to_freedom;
  const sbInterest = snowball.total_interest_paid;
  const moInterest = minimum_only.total_interest_paid;

  if (!sbMonths || !moMonths) return null;

  const months_saved    = moMonths - sbMonths;
  const interest_saved  = Math.round((moInterest - sbInterest) * 100) / 100;
  const years_saved     = +(months_saved / 12).toFixed(1);

  return {
    months_saved,
    years_saved,
    interest_saved,
    minimum_only_months:   moMonths,
    minimum_only_interest: moInterest,
    minimum_only_payoff:   minimum_only.payoff_display,
    snowball_months:       sbMonths,
    snowball_interest:     sbInterest,
    snowball_payoff:       snowball.payoff_display,
  };
}

// PATCH /api/snowball/order — batch-save snowball_order positions
router.patch('/order', (req, res) => {
  const items = req.body;
  if (!Array.isArray(items)) {
    return res.status(400).json(standardizeError('Request body must be an array', 'VALIDATION_ERROR'));
  }

  const db     = getDb();
  const userId = req.user.id;
  const update = db.prepare('UPDATE bills SET snowball_order = ? WHERE id = ? AND user_id = ?');

  db.transaction((rows) => {
    for (const row of rows) {
      const id    = parseInt(row.id, 10);
      const order = parseInt(row.snowball_order, 10);
      if (!Number.isInteger(id) || id <= 0) continue;
      if (!Number.isInteger(order) || order < 0) continue;
      update.run(order, id, userId);
    }
  })(items);

  res.json({ success: true });
});

module.exports = router;
