const express = require('express');
const router = express.Router();
const { getDb } = require('../db/database');
const { getCycleRange } = require('../services/statusService');
const { accountingActiveSql } = require('../services/paymentAccountingService');
const { toCents, fromCents } = require('../utils/money.mts');

function parseYearMonth(source) {
  const now = new Date();
  const year = parseInt(source.year || now.getFullYear(), 10);
  const month = parseInt(source.month || now.getMonth() + 1, 10);

  if (Number.isNaN(year) || year < 2000 || year > 2100) {
    return { error: 'year must be a 4-digit integer between 2000 and 2100' };
  }
  if (Number.isNaN(month) || month < 1 || month > 12) {
    return { error: 'month must be an integer between 1 and 12' };
  }

  return { year, month };
}

function money(value) {
  const n = Number(value);
  return Number.isFinite(n) ? Math.round(n * 100) / 100 : 0;
}

function getStartingAmounts(db, userId, year, month) {
  const row = db.prepare(`
    SELECT first_amount, fifteenth_amount, other_amount
    FROM monthly_starting_amounts
    WHERE user_id = ? AND year = ? AND month = ?
  `).get(userId, year, month);

  return {
    first_amount: fromCents(row?.first_amount || 0),
    fifteenth_amount: fromCents(row?.fifteenth_amount || 0),
    other_amount: fromCents(row?.other_amount || 0),
  };
}

function calculatePaidDeductions(db, userId, year, month) {
  const { start, end } = getCycleRange(year, month);

  // Paid from first bucket: bills with due_day 1-14
  const firstPaid = db.prepare(`
    SELECT COALESCE(SUM(p.amount), 0) AS paid
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    WHERE b.user_id = ?
      AND p.paid_date BETWEEN ? AND ?
      AND p.deleted_at IS NULL
      AND ${accountingActiveSql('p')}
      AND b.due_day BETWEEN 1 AND 14
  `).get(userId, start, end);

  // Paid from fifteenth bucket: bills with due_day 15-31
  const fifteenthPaid = db.prepare(`
    SELECT COALESCE(SUM(p.amount), 0) AS paid
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    WHERE b.user_id = ?
      AND p.paid_date BETWEEN ? AND ?
      AND p.deleted_at IS NULL
      AND ${accountingActiveSql('p')}
      AND b.due_day BETWEEN 15 AND 31
  `).get(userId, start, end);

  // Paid from other bucket: bills with due_day outside 1-14 and 15-31 (shouldn't happen with current schema)
  const otherPaid = db.prepare(`
    SELECT COALESCE(SUM(p.amount), 0) AS paid
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    WHERE b.user_id = ?
      AND p.paid_date BETWEEN ? AND ?
      AND p.deleted_at IS NULL
      AND ${accountingActiveSql('p')}
      AND (b.due_day < 1 OR b.due_day > 31)
  `).get(userId, start, end);

  const totalPaid = db.prepare(`
    SELECT COALESCE(SUM(p.amount), 0) AS paid
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    WHERE b.user_id = ?
      AND p.paid_date BETWEEN ? AND ?
      AND p.deleted_at IS NULL
      AND ${accountingActiveSql('p')}
  `).get(userId, start, end);

  return {
    paid_from_first: fromCents(firstPaid.paid),
    paid_from_fifteenth: fromCents(fifteenthPaid.paid),
    paid_from_other: fromCents(otherPaid.paid),
    paid_total: fromCents(totalPaid.paid),
  };
}

function buildStartingAmountsResponse(db, userId, year, month) {
  const amounts = getStartingAmounts(db, userId, year, month);
  const paid = calculatePaidDeductions(db, userId, year, month);

  const combined_amount = money(amounts.first_amount + amounts.fifteenth_amount + amounts.other_amount);
  const paid_total = paid.paid_total;

  return {
    year,
    month,
    first_amount: amounts.first_amount,
    fifteenth_amount: amounts.fifteenth_amount,
    other_amount: amounts.other_amount,
    combined_amount,
    paid_from_first: paid.paid_from_first,
    paid_from_fifteenth: paid.paid_from_fifteenth,
    paid_from_other: paid.paid_from_other,
    paid_total,
    first_remaining: money(amounts.first_amount - paid.paid_from_first),
    fifteenth_remaining: money(amounts.fifteenth_amount - paid.paid_from_fifteenth),
    other_remaining: money(amounts.other_amount - paid.paid_from_other),
    combined_remaining: money(combined_amount - paid_total),
  };
}

router.get('/', (req, res) => {
  const parsed = parseYearMonth(req.query);
  if (parsed.error) return res.status(400).json({ error: parsed.error });

  const db = getDb();
  res.json(buildStartingAmountsResponse(db, req.user.id, parsed.year, parsed.month));
});

router.put('/', (req, res) => {
  const parsed = parseYearMonth(req.body || {});
  if (parsed.error) return res.status(400).json({ error: parsed.error });

  const firstAmount = Number(req.body?.first_amount);
  if (!Number.isFinite(firstAmount) || firstAmount < 0 || firstAmount > 1000000000) {
    return res.status(400).json({ error: 'first_amount must be a number between 0 and 1000000000' });
  }

  const fifteenthAmount = Number(req.body?.fifteenth_amount);
  if (!Number.isFinite(fifteenthAmount) || fifteenthAmount < 0 || fifteenthAmount > 1000000000) {
    return res.status(400).json({ error: 'fifteenth_amount must be a number between 0 and 1000000000' });
  }

  const otherAmount = Number(req.body?.other_amount);
  if (!Number.isFinite(otherAmount) || otherAmount < 0 || otherAmount > 1000000000) {
    return res.status(400).json({ error: 'other_amount must be a number between 0 and 1000000000' });
  }

  const db = getDb();
  db.prepare(`
    INSERT INTO monthly_starting_amounts (user_id, year, month, first_amount, fifteenth_amount, other_amount, updated_at)
    VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
    ON CONFLICT(user_id, year, month) DO UPDATE SET
      first_amount = excluded.first_amount,
      fifteenth_amount = excluded.fifteenth_amount,
      other_amount = excluded.other_amount,
      updated_at = datetime('now')
  `).run(req.user.id, parsed.year, parsed.month, toCents(firstAmount), toCents(fifteenthAmount), toCents(otherAmount));

  res.json(buildStartingAmountsResponse(db, req.user.id, parsed.year, parsed.month));
});

module.exports = router;
