// @ts-nocheck — route controller converted to .cts; handler req/res typed, full type-check deferred (thin glue over typed services).
import type { Req, Res, Next } from '../types/http';
('use strict');

const express = require('express');
const router = express.Router();
const { getDb } = require('../db/database.cts');
const {
  getSpendingSummary,
  getSpendingTransactions,
  categorizeTransaction,
  getSpendingBudgets,
  setSpendingBudget,
  getSpendingCategoryRules,
  addSpendingCategoryRule,
  deleteSpendingCategoryRule,
  getIncomeTransactions,
} = require('../services/spendingService.cts');

function parseYM(source) {
  const now = new Date();
  const year = parseInt(source.year || now.getFullYear(), 10);
  const month = parseInt(source.month || now.getMonth() + 1, 10);
  if (isNaN(year) || year < 2000 || year > 2100) return { error: 'Invalid year' };
  if (isNaN(month) || month < 1 || month > 12) return { error: 'Invalid month' };
  return { year, month };
}

// GET /api/spending/summary?year=&month=
router.get('/summary', (req: Req, res: Res) => {
  const ym = parseYM(req.query);
  if (ym.error) return res.status(400).json({ error: ym.error });
  try {
    res.json(getSpendingSummary(getDb(), req.user.id, ym.year, ym.month));
  } catch (err) {
    console.error('[spending/summary]', err.message);
    res.status(500).json({ error: 'Failed to load spending summary' });
  }
});

// GET /api/spending/transactions?year=&month=&category_id=&page=&limit=
router.get('/transactions', (req: Req, res: Res) => {
  const ym = parseYM(req.query);
  if (ym.error) return res.status(400).json({ error: ym.error });

  const { category_id, page, limit } = req.query;
  const categoryId =
    category_id === 'null'
      ? null
      : category_id !== undefined
        ? parseInt(category_id, 10)
        : undefined;

  try {
    res.json(
      getSpendingTransactions(getDb(), req.user.id, ym.year, ym.month, {
        categoryId,
        uncategorizedOnly: category_id === 'null',
        page: parseInt(page || '1', 10),
        limit: Math.min(parseInt(limit || '50', 10), 200),
      }),
    );
  } catch (err) {
    console.error('[spending/transactions]', err.message);
    res.status(500).json({ error: 'Failed to load transactions' });
  }
});

// PATCH /api/spending/transactions/:id/category
router.patch('/transactions/:id/category', (req: Req, res: Res) => {
  const txId = parseInt(req.params.id, 10);
  if (isNaN(txId)) return res.status(400).json({ error: 'Invalid transaction ID' });

  const { category_id, save_rule } = req.body || {};
  const categoryId =
    category_id === null || category_id === undefined ? null : parseInt(category_id, 10);

  try {
    categorizeTransaction(getDb(), req.user.id, txId, categoryId, !!save_rule);
    res.json({ ok: true });
  } catch (err) {
    res
      .status(err.status || 500)
      .json({ error: err.message || 'Failed to categorize transaction' });
  }
});

// GET /api/spending/budgets?year=&month=
router.get('/budgets', (req: Req, res: Res) => {
  const ym = parseYM(req.query);
  if (ym.error) return res.status(400).json({ error: ym.error });
  try {
    res.json({ budgets: getSpendingBudgets(getDb(), req.user.id, ym.year, ym.month) });
  } catch (err) {
    console.error('[spending/budgets GET]', err.message);
    res.status(500).json({ error: 'Failed to load budgets' });
  }
});

// POST /api/spending/budgets/copy — copy all budgets from previous month into target month
router.post('/budgets/copy', (req: Req, res: Res) => {
  const ym = parseYM(req.body || {});
  if (ym.error) return res.status(400).json({ error: ym.error });

  // Previous month
  let prevYear = ym.year,
    prevMonth = ym.month - 1;
  if (prevMonth < 1) {
    prevMonth = 12;
    prevYear--;
  }

  try {
    const db = getDb();
    const prev = db
      .prepare(
        `
      SELECT category_id, amount FROM spending_budgets
      WHERE user_id = ? AND year = ? AND month = ?
    `,
      )
      .all(req.user.id, prevYear, prevMonth);

    if (prev.length === 0) {
      return res.json({
        copied: 0,
        budgets: getSpendingBudgets(db, req.user.id, ym.year, ym.month),
      });
    }

    const upsert = db.prepare(`
      INSERT INTO spending_budgets (user_id, category_id, year, month, amount, updated_at)
      VALUES (?, ?, ?, ?, ?, datetime('now'))
      ON CONFLICT(user_id, category_id, year, month) DO UPDATE SET
        amount = excluded.amount,
        updated_at = datetime('now')
    `);

    db.transaction(() => {
      for (const row of prev)
        upsert.run(req.user.id, row.category_id, ym.year, ym.month, row.amount);
    })();

    res.json({
      copied: prev.length,
      budgets: getSpendingBudgets(db, req.user.id, ym.year, ym.month),
    });
  } catch (err) {
    console.error('[spending/budgets/copy]', err.message);
    res.status(500).json({ error: 'Failed to copy budgets' });
  }
});

// PUT /api/spending/budgets — { category_id, year, month, amount }
router.put('/budgets', (req: Req, res: Res) => {
  const { category_id, year, month, amount } = req.body || {};
  if (!category_id) return res.status(400).json({ error: 'category_id required' });
  const ym = parseYM({ year, month });
  if (ym.error) return res.status(400).json({ error: ym.error });

  try {
    setSpendingBudget(
      getDb(),
      req.user.id,
      parseInt(category_id, 10),
      ym.year,
      ym.month,
      amount ?? null,
    );
    res.json({ ok: true });
  } catch (err) {
    console.error('[spending/budgets PUT]', err.message);
    res.status(500).json({ error: 'Failed to save budget' });
  }
});

// GET /api/spending/category-rules
router.get('/category-rules', (req: Req, res: Res) => {
  try {
    res.json({ rules: getSpendingCategoryRules(getDb(), req.user.id) });
  } catch (err) {
    console.error('[spending/category-rules GET]', err.message);
    res.status(500).json({ error: 'Failed to load rules' });
  }
});

// POST /api/spending/category-rules — { category_id, merchant }
router.post('/category-rules', (req: Req, res: Res) => {
  const { category_id, merchant } = req.body || {};
  if (!category_id || !merchant)
    return res.status(400).json({ error: 'category_id and merchant required' });
  try {
    addSpendingCategoryRule(getDb(), req.user.id, parseInt(category_id, 10), merchant);
    res.json({ ok: true });
  } catch (err) {
    console.error('[spending/category-rules POST]', err.message);
    res.status(err.status || 500).json({ error: err.message || 'Failed to save rule' });
  }
});

// DELETE /api/spending/category-rules/:id
router.delete('/category-rules/:id', (req: Req, res: Res) => {
  try {
    deleteSpendingCategoryRule(getDb(), req.user.id, parseInt(req.params.id, 10));
    res.json({ ok: true });
  } catch (err) {
    console.error('[spending/category-rules DELETE]', err.message);
    res.status(500).json({ error: 'Failed to delete rule' });
  }
});

// GET /api/spending/income?year=&month=&page=
router.get('/income', (req: Req, res: Res) => {
  const ym = parseYM(req.query);
  if (ym.error) return res.status(400).json({ error: ym.error });
  try {
    res.json(
      getIncomeTransactions(getDb(), req.user.id, ym.year, ym.month, {
        page: parseInt(req.query.page || '1', 10),
        limit: Math.min(parseInt(req.query.limit || '50', 10), 200),
        includeIgnored: req.query.include_ignored === 'true',
      }),
    );
  } catch (err) {
    console.error('[spending/income]', err.message);
    res.status(500).json({ error: 'Failed to load income transactions' });
  }
});

module.exports = router;
