const express = require('express');
const router  = express.Router();
const { getDb } = require('../db/database');
const { buildTrackerRow, getCycleRange, resolveDueDate } = require('../services/statusService');
const { getUserSettings } = require('../services/userSettings');

// GET /api/tracker?year=2026&month=5
router.get('/', (req, res) => {
  const db = getDb();
  const now   = new Date();
  const year  = parseInt(req.query.year  || now.getFullYear(),    10);
  const month = parseInt(req.query.month || now.getMonth() + 1,   10);

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

  const todayStr = now.toISOString().slice(0, 10);
  const userSettings = getUserSettings(req.user.id);
  const rowOptions = { gracePeriodDays: userSettings.grace_period_days };

  const { start, end } = getCycleRange(year, month);

  // Calculate previous month (with year wrapping)
  const prevMonth = month === 1 ? 12 : month - 1;
  const prevYear = month === 1 ? year - 1 : year;
  const prevMonthRange = getCycleRange(prevYear, prevMonth);

  // Calculate 3-month range for trend analysis
  const threeMonthsAgo = (() => {
    let y = year, m = month - 2;
    while (m <= 0) { m += 12; y -= 1; }
    return { year: y, month: m };
  })();

  const bills = db.prepare(`
    SELECT b.*, c.name AS category_name
    FROM bills b
    LEFT JOIN categories c ON b.category_id = c.id AND c.deleted_at IS NULL
    WHERE b.active = 1 AND b.user_id = ? AND b.deleted_at IS NULL
    ORDER BY b.due_day ASC, b.name ASC
  `).all(req.user.id);

  // Batch fetch all monthly bill states for current month
  const billIds = bills.map(bill => bill.id);
  const placeholders = billIds.map(() => '?').join(',');
  
  let monthlyStates = {};
  if (billIds.length > 0) {
    const monthlyStateQuery = `
      SELECT bill_id, actual_amount, notes, is_skipped 
      FROM monthly_bill_state 
      WHERE bill_id IN (${placeholders}) AND year = ? AND month = ?
    `;
    const monthlyStateRows = db.prepare(monthlyStateQuery).all(...billIds, year, month);
    monthlyStates = Object.fromEntries(monthlyStateRows.map(row => [row.bill_id, row]));
  }

  // Batch fetch all payments for current month
  let allPayments = {};
  if (billIds.length > 0) {
    const paymentsQuery = `
      SELECT bill_id, id, amount, paid_date, method, notes, created_at, updated_at
      FROM payments
      WHERE bill_id IN (${placeholders}) AND paid_date BETWEEN ? AND ?
        AND deleted_at IS NULL
      ORDER BY paid_date DESC
    `;
    const paymentRows = db.prepare(paymentsQuery).all(...billIds, start, end);
    
    // Group payments by bill_id
    allPayments = {};
    paymentRows.forEach(row => {
      if (!allPayments[row.bill_id]) {
        allPayments[row.bill_id] = [];
      }
      allPayments[row.bill_id].push(row);
    });
  }

  // Batch fetch all previous month payments
  let prevMonthPayments = {};
  if (billIds.length > 0) {
    const prevPaymentsQuery = `
      SELECT bill_id, SUM(amount) as total_paid
      FROM payments
      WHERE bill_id IN (${placeholders}) AND paid_date BETWEEN ? AND ?
        AND deleted_at IS NULL
      GROUP BY bill_id
    `;
    const prevPaymentRows = db.prepare(prevPaymentsQuery).all(...billIds, prevMonthRange.start, prevMonthRange.end);
    prevMonthPayments = Object.fromEntries(prevPaymentRows.map(row => [row.bill_id, row.total_paid]));
  }

  const rows = bills.map(bill => {
    // Get payments for this bill
    const payments = allPayments[bill.id] || [];

    const row = buildTrackerRow(bill, payments, year, month, todayStr, rowOptions);

    // Overlay monthly state overrides
    const mbs = monthlyStates[bill.id];
    row.actual_amount  = mbs?.actual_amount  ?? null;
    row.monthly_notes  = mbs?.notes          ?? null;
    row.is_skipped     = !!(mbs?.is_skipped);

    // Get previous month paid amount
    row.previous_month_paid = prevMonthPayments[bill.id] || 0;

    return row;
  });

  const totalOverdue  = rows
    .filter(r => !r.is_skipped && (r.status === 'late' || r.status === 'missed'))
    .reduce((s, r) => s + r.balance, 0);

  const activeRows = rows.filter(r => !r.is_skipped);

  // Get starting amounts for this month
  const startingAmounts = db.prepare(`
    SELECT COALESCE(first_amount, 0) AS first_amount,
           COALESCE(fifteenth_amount, 0) AS fifteenth_amount,
           COALESCE(other_amount, 0) AS other_amount,
           COALESCE(first_amount, 0) + COALESCE(fifteenth_amount, 0) + COALESCE(other_amount, 0) AS combined_amount
    FROM monthly_starting_amounts
    WHERE user_id = ? AND year = ? AND month = ?
  `).get(req.user.id, year, month);

  const dayOfMonth = now.getDate();
  const activeRemainingPeriod = dayOfMonth < 15 ? '1st' : '15th';
  const periodRows = activeRows.filter(r => r.bucket === activeRemainingPeriod);
  const periodPaid = periodRows.reduce((s, r) => s + r.total_paid, 0);
  const periodOutstandingBalance = periodRows.reduce((s, r) => s + Math.max(r.balance || 0, 0), 0);
  const periodStartingAmount = activeRemainingPeriod === '1st'
    ? (startingAmounts?.first_amount || 0)
    : (startingAmounts?.fifteenth_amount || 0);
  const periodLabel = activeRemainingPeriod === '1st' ? '1st balance' : '15th balance';

  const totalStarting = startingAmounts?.combined_amount || 0;
  const hasStartingAmounts = !!startingAmounts;
  const activeTotalPaid = activeRows.reduce((s, r) => s + r.total_paid, 0);
  const activeTotalExpected = activeRows.reduce((s, r) => s + r.expected_amount, 0);
  const activeOutstandingBalance = activeRows.reduce((s, r) => s + Math.max(r.balance || 0, 0), 0);
  
  // Calculate previous month total
  const previousMonthTotal = activeRows.reduce((s, r) => s + r.previous_month_paid, 0);

  // Calculate 3-month trend data
  const threeMonthStart = getCycleRange(threeMonthsAgo.year, threeMonthsAgo.month).start;
  const currentMonthEnd = end;
  
  // Get all payments for the last 3 months for this user
  // Join through bills to get user_id since payments table doesn't have user_id
  const threeMonthPayments = db.prepare(`
    SELECT SUM(p.amount) as total_paid, strftime('%Y-%m', p.paid_date) as month_key
    FROM payments p
    JOIN bills b ON p.bill_id = b.id
    WHERE b.user_id = ? AND b.deleted_at IS NULL AND p.paid_date BETWEEN ? AND ?
      AND p.deleted_at IS NULL
    GROUP BY strftime('%Y-%m', p.paid_date)
  `).all(req.user.id, threeMonthStart, currentMonthEnd);

  // Create a map of month payments for easier access
  const monthlyPaymentsMap = new Map();
  threeMonthPayments.forEach(payment => {
    monthlyPaymentsMap.set(payment.month_key, payment.total_paid);
  });

  // Calculate payments for each of the last 3 months
  const months = [];
  for (let i = 2; i >= 0; i--) {
    const date = new Date(year, month - 1 - i);
    const monthKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
    months.push({
      year: date.getFullYear(),
      month: date.getMonth() + 1,
      key: monthKey,
      payment: parseFloat(monthlyPaymentsMap.get(monthKey) || 0)
    });
  }

  // Calculate 3-month average
  const threeMonthTotal = months.reduce((sum, m) => sum + m.payment, 0);
  const threeMonthAvg = threeMonthTotal / 3;

  // Calculate current month paid (sum of all bills)
  const currentMonthPaid = activeTotalPaid;

  // Calculate percentage change
  let percentChange = 0;
  let direction = 'flat';
  
  if (threeMonthAvg > 0) {
    percentChange = ((currentMonthPaid - threeMonthAvg) / threeMonthAvg) * 100;
    
    // Determine direction based on percentage change
    if (percentChange > 2) {
      direction = 'up';
    } else if (percentChange < -2) {
      direction = 'down';
    } else {
      direction = 'flat';
    }
  }

  // Ensure percentChange is a number with 1 decimal place
  percentChange = parseFloat(percentChange.toFixed(1));

  res.json({
    year, month, today: todayStr,
    summary: {
      total_expected:  activeTotalExpected,
      total_starting:  totalStarting,
      has_starting_amounts: hasStartingAmounts,
      total_paid:      activeTotalPaid,
      remaining:       hasStartingAmounts ? periodStartingAmount - periodPaid : periodOutstandingBalance,
      total_remaining: hasStartingAmounts ? totalStarting - activeTotalPaid : activeOutstandingBalance,
      remaining_period: activeRemainingPeriod,
      remaining_label: periodLabel,
      remaining_hint:  hasStartingAmounts
        ? `${periodLabel}: ${periodStartingAmount.toFixed(2)} starting minus ${periodPaid.toFixed(2)} paid`
        : `${periodLabel}: unpaid bills due in this period`,
      overdue:         totalOverdue,
      count_paid:      activeRows.filter(r => r.status === 'paid').length,
      count_upcoming:  activeRows.filter(r => r.status === 'upcoming' || r.status === 'due_soon').length,
      count_late:      activeRows.filter(r => r.status === 'late' || r.status === 'missed').length,
      count_autodraft: activeRows.filter(r => r.status === 'autodraft').length,
      previous_month_total: previousMonthTotal,
      trend: {
        three_month_avg: parseFloat(threeMonthAvg.toFixed(2)),
        current_month_paid: parseFloat(currentMonthPaid.toFixed(2)),
        percent_change: percentChange,
        direction: direction
      }
    },
    rows,
  });
});

// GET /api/tracker/upcoming?days=30
// Returns active bills with a due date in the next N days, sorted by due_date asc.
router.get('/upcoming', (req, res) => {
  const db   = getDb();
  const days = Math.max(1, Math.min(parseInt(req.query.days || '30', 10) || 30, 365));
  const now  = new Date();
  const todayStr = now.toISOString().slice(0, 10);
  const userSettings = getUserSettings(req.user.id);
  const rowOptions = { gracePeriodDays: userSettings.grace_period_days };

  const year  = now.getFullYear();
  const month = now.getMonth() + 1;
  const { start, end } = getCycleRange(year, month);

  const bills = db.prepare(`
    SELECT b.*, c.name AS category_name
    FROM bills b
    LEFT JOIN categories c ON b.category_id = c.id AND c.deleted_at IS NULL
    WHERE b.active = 1 AND b.user_id = ? AND b.deleted_at IS NULL
  `).all(req.user.id);

  const cutoff = new Date(now);
  cutoff.setDate(cutoff.getDate() + days);
  const cutoffStr = cutoff.toISOString().slice(0, 10);

  // Get all bill IDs for batch processing
  const billIds = bills.map(bill => bill.id);
  
  // Batch fetch all payments for all bills in the date range
  let allPayments = {};
  if (billIds.length > 0) {
    const placeholders = billIds.map(() => '?').join(',');
    const paymentsQuery = `
      SELECT bill_id, id, amount, paid_date, method, notes, created_at, updated_at
      FROM payments
      WHERE bill_id IN (${placeholders}) AND paid_date BETWEEN ? AND ?
        AND deleted_at IS NULL
      ORDER BY paid_date DESC
    `;
    const paymentRows = db.prepare(paymentsQuery).all(...billIds, start, end);
    
    // Group payments by bill_id
    allPayments = {};
    paymentRows.forEach(row => {
      if (!allPayments[row.bill_id]) {
        allPayments[row.bill_id] = [];
      }
      allPayments[row.bill_id].push(row);
    });
  }

  const upcoming = [];

  for (const bill of bills) {
    const dueDate = resolveDueDate(bill, year, month);
    if (dueDate < todayStr || dueDate > cutoffStr) continue;

    // Get payments for this bill from the batched results
    const payments = allPayments[bill.id] || [];

    const row = buildTrackerRow(bill, payments, year, month, todayStr, rowOptions);
    if (row.status === 'paid') continue; // skip already paid

    upcoming.push({
      id:              bill.id,
      name:            bill.name,
      category_name:   bill.category_name,
      due_date:        dueDate,
      expected_amount: bill.expected_amount,
      status:          row.status,
      days_until_due:  Math.floor((new Date(dueDate) - now) / 86400000),
    });
  }

  upcoming.sort((a, b) => a.due_date.localeCompare(b.due_date));
  res.json({ days, today: todayStr, upcoming });
});

module.exports = router;
