const express = require('express');
const { standardizeError } = require('../middleware/errorFormatter');
const router  = express.Router();
const os = require('os');
const path = require('path');
const fs = require('fs');
const Database = require('better-sqlite3');
const xlsx = require('xlsx');
const { getDb } = require('../db/database');
const { fromCents } = require('../utils/money.mts');

// GET /api/export?year=2026&format=csv   — or a date range: ?from=YYYY-MM-DD&to=YYYY-MM-DD
router.get('/', (req, res) => {
  const db     = getDb();
  const format = (req.query.format || 'csv').toLowerCase();
  const { from, to } = req.query;
  const isDate = (s) => typeof s === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(s);

  // Filter by an explicit date range (both bounds required) or by a single year.
  let where, whereParams, label;
  if (from != null || to != null) {
    if (!isDate(from) || !isDate(to)) {
      return res.status(400).json(standardizeError('from and to must both be YYYY-MM-DD dates', 'VALIDATION_ERROR', 'from'));
    }
    if (from > to) {
      return res.status(400).json(standardizeError('from must be on or before to', 'VALIDATION_ERROR', 'from'));
    }
    where = 'p.paid_date BETWEEN ? AND ?';
    whereParams = [from, to];
    label = `${from}_to_${to}`;
  } else {
    const year = parseInt(req.query.year || new Date().getFullYear(), 10);
    if (isNaN(year) || year < 2000 || year > 2100) {
      return res.status(400).json(standardizeError('year must be a 4-digit integer between 2000 and 2100', 'VALIDATION_ERROR', 'year'));
    }
    where = "strftime('%Y', p.paid_date) = ?";
    whereParams = [String(year)];
    label = String(year);
  }

  const rows = db.prepare(`
    SELECT
      p.paid_date,
      p.bill_id,
      b.name        AS bill_name,
      c.name        AS category,
      b.expected_amount,
      p.amount      AS paid_amount,
      p.method,
      p.notes,
      p.created_at
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    LEFT JOIN categories c ON c.id = b.category_id AND c.deleted_at IS NULL
    WHERE ${where}
      AND b.user_id = ?
      AND b.deleted_at IS NULL
      AND p.deleted_at IS NULL
    ORDER BY p.paid_date ASC, b.name ASC
  `).all(...whereParams, req.user.id);

  const mbsStmt = db.prepare(
    'SELECT actual_amount, notes FROM monthly_bill_state WHERE bill_id=? AND year=? AND month=?'
  );

  if (format === 'csv') {
    const header = 'Date,Bill,Category,Expected,Paid,Method,Notes,Actual Amount,Monthly Notes\n';
    const escCsv = (v) => {
      if (v == null) return '';
      const s = String(v);
      return /[,"\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
    };

    const body = rows.map(r => {
      const paidMonth = parseInt(r.paid_date.slice(5, 7), 10);
      const paidYear  = parseInt(r.paid_date.slice(0, 4), 10);
      const mbs = mbsStmt.get(r.bill_id, paidYear, paidMonth);
      return [
        r.paid_date,
        escCsv(r.bill_name),
        escCsv(r.category),
        fromCents(r.expected_amount).toFixed(2),
        fromCents(r.paid_amount).toFixed(2),
        escCsv(r.method),
        escCsv(r.notes),
        mbs?.actual_amount != null ? fromCents(mbs.actual_amount).toFixed(2) : '',
        escCsv(mbs?.notes ?? null),
      ].join(',');
    }).join('\n');

    res.setHeader('Content-Type', 'text/csv');
    res.setHeader('Content-Disposition', `attachment; filename="bills-${label}.csv"`);
    return res.send(header + body);
  }

  // Fallback: JSON — enrich each row with monthly_bill_state overrides
  const enriched = rows.map(r => {
    const paidMonth = parseInt(r.paid_date.slice(5, 7), 10);
    const paidYear  = parseInt(r.paid_date.slice(0, 4), 10);
    const mbs = mbsStmt.get(r.bill_id, paidYear, paidMonth);
    return {
      ...r,
      expected_amount: fromCents(r.expected_amount),
      paid_amount:    fromCents(r.paid_amount),
      actual_amount:  fromCents(mbs?.actual_amount ?? null),
      monthly_notes:  mbs?.notes         ?? null,
    };
  });
  res.json({ range: label, count: enriched.length, payments: enriched });
});

function getUserExportData(userId) {
  const db = getDb();
  const categories = db.prepare('SELECT id, name, created_at, updated_at FROM categories WHERE user_id = ? AND deleted_at IS NULL ORDER BY name').all(userId);
  const bills = db.prepare(`
    SELECT id, name, category_id, due_day, override_due_date, bucket, expected_amount, interest_rate,
           billing_cycle, cycle_type, cycle_day, autopay_enabled, autodraft_status, website, username,
           account_info, has_2fa, active, notes, created_at, updated_at
    FROM bills
    WHERE user_id = ? AND deleted_at IS NULL
    ORDER BY active DESC, due_day ASC, name ASC
  `).all(userId).map(b => ({ ...b, expected_amount: fromCents(b.expected_amount) }));
  const payments = db.prepare(`
    SELECT p.id, p.bill_id, p.amount, p.paid_date, p.method, p.notes,
           CASE WHEN p.payment_source = 'transaction_match' THEN 'manual' ELSE p.payment_source END AS payment_source,
           NULL AS transaction_id, p.created_at, p.updated_at
    FROM payments p
    JOIN bills b ON b.id = p.bill_id
    WHERE b.user_id = ? AND b.deleted_at IS NULL AND p.deleted_at IS NULL
    ORDER BY p.paid_date ASC, p.id ASC
  `).all(userId).map(p => ({ ...p, amount: fromCents(p.amount) }));
  const monthlyState = db.prepare(`
    SELECT m.id, m.bill_id, m.year, m.month, m.actual_amount, m.notes, m.is_skipped, m.created_at, m.updated_at
    FROM monthly_bill_state m
    JOIN bills b ON b.id = m.bill_id
    WHERE b.user_id = ? AND b.deleted_at IS NULL
    ORDER BY m.year, m.month, m.bill_id
  `).all(userId).map(m => ({ ...m, actual_amount: fromCents(m.actual_amount) }));
  const monthlyStartingAmounts = db.prepare(`
    SELECT id, year, month, first_amount, fifteenth_amount, other_amount, notes, created_at, updated_at
    FROM monthly_starting_amounts
    WHERE user_id = ?
    ORDER BY year, month
  `).all(userId).map(r => ({
    ...r,
    first_amount: fromCents(r.first_amount),
    fifteenth_amount: fromCents(r.fifteenth_amount),
    other_amount: fromCents(r.other_amount),
  }));
  const historyRanges = db.prepare(`
    SELECT id, bill_id, start_year, start_month, end_year, end_month, label, created_at, updated_at
    FROM bill_history_ranges
    WHERE bill_id IN (SELECT id FROM bills WHERE user_id = ? AND deleted_at IS NULL)
    ORDER BY bill_id, start_year, start_month
  `).all(userId);
  const notes = [
    ...bills.filter(b => b.notes).map(b => ({ type: 'bill', bill_id: b.id, notes: b.notes })),
    ...payments.filter(p => p.notes).map(p => ({ type: 'payment', payment_id: p.id, bill_id: p.bill_id, notes: p.notes })),
    ...monthlyState.filter(m => m.notes).map(m => ({ type: 'monthly_state', monthly_state_id: m.id, bill_id: m.bill_id, year: m.year, month: m.month, notes: m.notes })),
  ];
  const metadata = {
    exported_at: new Date().toISOString(),
    export_type: 'user_data',
    includes: ['Bills', 'Payments', 'Categories', 'Monthly bill state', 'Monthly starting amounts', 'Bill history ranges', 'Notes', 'Export metadata'],
    counts: {
      bills: bills.length,
      payments: payments.length,
      categories: categories.length,
      monthly_bill_state: monthlyState.length,
      monthly_starting_amounts: monthlyStartingAmounts.length,
      bill_history_ranges: historyRanges.length,
      notes: notes.length,
    },
  };
  return { metadata, categories, bills, payments, monthly_bill_state: monthlyState, monthly_starting_amounts: monthlyStartingAmounts, bill_history_ranges: historyRanges, notes };
}

router.get('/user-excel', (req, res) => {
  const data = getUserExportData(req.user.id);
  const wb = xlsx.utils.book_new();
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet([data.metadata]), 'Export Metadata');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.bills), 'Bills');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.payments), 'Payments');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.categories), 'Categories');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.monthly_bill_state), 'Monthly State');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.monthly_starting_amounts), 'Monthly Starting Amounts');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.bill_history_ranges), 'History Ranges');
  xlsx.utils.book_append_sheet(wb, xlsx.utils.json_to_sheet(data.notes), 'Notes');
  const buffer = xlsx.write(wb, { type: 'buffer', bookType: 'xlsx' });
  res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
  res.setHeader('Content-Disposition', 'attachment; filename="bill-tracker-user-export.xlsx"');
  res.send(buffer);
});

// Build the SQLite user-DB export to a temp file and return its path. Extracted
// so the export→import round-trip can be regression-tested (exportImportRoundTrip).
function buildUserDbExportFile(userId) {
  const data = getUserExportData(userId);
  const file = path.join(os.tmpdir(), `bill-tracker-user-${userId}-${Date.now()}.sqlite`);
  const out = new Database(file);
  try {
    out.exec(`
      CREATE TABLE export_metadata (key TEXT PRIMARY KEY, value TEXT);
      CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT, created_at TEXT, updated_at TEXT);
      CREATE TABLE bills (id INTEGER PRIMARY KEY, name TEXT, category_id INTEGER, due_day INTEGER, override_due_date TEXT, bucket TEXT, expected_amount REAL, interest_rate REAL, billing_cycle TEXT, cycle_type TEXT, cycle_day TEXT, autopay_enabled INTEGER, autodraft_status TEXT, website TEXT, username TEXT, account_info TEXT, has_2fa INTEGER, active INTEGER, notes TEXT, created_at TEXT, updated_at TEXT);
      CREATE TABLE payments (id INTEGER PRIMARY KEY, bill_id INTEGER, amount REAL, paid_date TEXT, method TEXT, notes TEXT, payment_source TEXT, transaction_id INTEGER, created_at TEXT, updated_at TEXT);
      CREATE TABLE monthly_bill_state (id INTEGER PRIMARY KEY, bill_id INTEGER, year INTEGER, month INTEGER, actual_amount REAL, notes TEXT, is_skipped INTEGER, created_at TEXT, updated_at TEXT);
      CREATE TABLE monthly_starting_amounts (id INTEGER PRIMARY KEY, year INTEGER, month INTEGER, first_amount REAL, fifteenth_amount REAL, other_amount REAL, notes TEXT, created_at TEXT, updated_at TEXT);
      CREATE TABLE bill_history_ranges (id INTEGER PRIMARY KEY, bill_id INTEGER, start_year INTEGER, start_month INTEGER, end_year INTEGER, end_month INTEGER, label TEXT, created_at TEXT, updated_at TEXT);
      CREATE TABLE notes (type TEXT, bill_id INTEGER, payment_id INTEGER, monthly_state_id INTEGER, year INTEGER, month INTEGER, notes TEXT);
    `);
    const meta = out.prepare('INSERT INTO export_metadata (key, value) VALUES (?, ?)');
    meta.run('metadata_json', JSON.stringify(data.metadata));
    const insertRows = (table, rows) => {
      if (!rows.length) return;
      const cols = Object.keys(rows[0]);
      const stmt = out.prepare(`INSERT INTO ${table} (${cols.join(',')}) VALUES (${cols.map(() => '?').join(',')})`);
      const tx = out.transaction((items) => items.forEach(row => stmt.run(cols.map(c => row[c]))));
      tx(rows);
    };
    insertRows('categories', data.categories);
    insertRows('bills', data.bills);
    insertRows('payments', data.payments);
    insertRows('monthly_bill_state', data.monthly_bill_state);
    insertRows('monthly_starting_amounts', data.monthly_starting_amounts);
    insertRows('bill_history_ranges', data.bill_history_ranges);
    insertRows('notes', data.notes.map(n => ({
      type: n.type,
      bill_id: n.bill_id ?? null,
      payment_id: n.payment_id ?? null,
      monthly_state_id: n.monthly_state_id ?? null,
      year: n.year ?? null,
      month: n.month ?? null,
      notes: n.notes,
    })));
  } finally {
    out.close();
  }
  return file;
}

router.get('/user-db', (req, res) => {
  const file = buildUserDbExportFile(req.user.id);
  res.download(file, 'bill-tracker-user-export.sqlite', () => {
    try { fs.unlinkSync(file); } catch {}
  });
});

// Full portable JSON export of the user's data (same assembly as SQLite/Excel).
router.get('/user-json', (req, res) => {
  const data = getUserExportData(req.user.id);
  res.setHeader('Content-Type', 'application/json');
  res.setHeader('Content-Disposition', 'attachment; filename="bill-tracker-user-export.json"');
  res.send(JSON.stringify(data, null, 2));
});

module.exports = router;
module.exports.buildUserDbExportFile = buildUserDbExportFile;
module.exports.getUserExportData = getUserExportData;
