const express      = require('express');
const cookieParser = require('cookie-parser');
const path         = require('path');

const { getDb }                                   = require('./db/database');
const { requireAuth, requireUser, requireAdmin }  = require('./middleware/requireAuth');
const { recordError }                             = require('./services/statusRuntime');
const { securityHeaders }                         = require('./middleware/securityHeaders');
const { importLimiter, exportLimiter, adminActionLimiter, oidcLimiter } =
  require('./middleware/rateLimiter');

const app  = express();
const PORT = process.env.PORT || 3000;
const DIST = path.join(__dirname, 'dist');

// ── Security headers (applied to every response) ──────────────────────────────
app.use(securityHeaders);

// ── CORS — disabled unless CORS_ORIGIN is explicitly configured ───────────────
// The default same-origin deployment (Express serves both API and UI) needs no
// CORS headers. Set CORS_ORIGIN=https://your-frontend.example.com only when
// the frontend is hosted on a separate origin.
if (process.env.CORS_ORIGIN) {
  const cors    = require('cors');
  const allowed = process.env.CORS_ORIGIN.split(',').map(s => s.trim()).filter(Boolean);
  app.use(cors({ credentials: true, origin: allowed }));
}

app.use(express.json());
app.use(cookieParser());

// ── API ───────────────────────────────────────────────────────────────────────

// Auth — login and password-change rate limits are applied inside the route file
app.use('/api/auth',         require('./routes/auth'));

// OIDC — rate-limited; returns 501 gracefully when OIDC is not configured
app.use('/api/auth/oidc',    oidcLimiter, require('./routes/authOidc'));

// Admin — all routes already require auth+admin; mutation-heavy routes get
// an additional per-IP rate limit applied to the whole admin namespace
app.use('/api/admin',        requireAuth, requireAdmin, adminActionLimiter, require('./routes/admin'));

app.use('/api/tracker',      requireAuth, requireUser,  require('./routes/tracker'));
app.use('/api/bills',        requireAuth, requireUser,  require('./routes/bills'));
app.use('/api/payments',     requireAuth, requireUser,  require('./routes/payments'));
app.use('/api/categories',   requireAuth, requireUser,  require('./routes/categories'));
app.use('/api/settings',     requireAuth, requireUser,  require('./routes/settings'));
app.use('/api/calendar',     requireAuth, requireUser,  require('./routes/calendar'));
app.use('/api/analytics',    requireAuth, requireUser,  require('./routes/analytics'));
app.use('/api/notifications', requireAuth,              require('./routes/notifications'));
app.use('/api/status',        requireAuth,              require('./routes/status'));
app.use('/api/version',                                require('./routes/version'));   // public

// Profile — password-change rate limit applied inside the route file
app.use('/api/profile',      requireAuth, requireUser,  require('./routes/profile'));

// Export / Import — per-IP rate limited to deter abuse and resource exhaustion
app.use('/api/export',       requireAuth, requireUser,  exportLimiter, require('./routes/export'));
app.use('/api/import',       requireAuth, requireUser,  importLimiter, require('./routes/import'));

// ── Legacy UI ("Remember When" mode) ─────────────────────────────────────────
app.use('/legacy', express.static(path.join(__dirname, 'legacy')));

// ── Modern UI (Vite build) ────────────────────────────────────────────────────
app.get('/login.html', (req, res) => res.redirect(302, '/login'));
app.use(express.static(DIST));
app.get('*', (req, res) => res.sendFile(path.join(DIST, 'index.html')));

// ── Global error handler ──────────────────────────────────────────────────────
// Never expose stack traces, internal paths, or raw error objects in responses.
app.use((err, req, res, next) => {
  recordError('Express', err);
  console.error('[error]', err.message || String(err));

  if (req.path?.startsWith('/api/import/')) {
    const isBodyError = err.type === 'entity.too.large' || err instanceof SyntaxError;
    return res.status(err.status || (isBodyError ? 400 : 500)).json({
      error: 'Import request failed',
      message: isBodyError
        ? 'The import request could not be read. Please retry with a smaller or valid file.'
        : 'Unexpected import server error. Please try again.',
      code: isBodyError ? 'IMPORT_REQUEST_ERROR' : 'IMPORT_SERVER_ERROR',
    });
  }

  res.status(err.status || 500).json({
    error: err.status ? err.message : 'Internal server error',
  });
});

// ── Bootstrap ─────────────────────────────────────────────────────────────────
async function main() {
  const db = getDb();
  const userCount = db.prepare('SELECT COUNT(*) AS count FROM users').get().count;
  if (userCount === 0) await require('./setup/firstRun').run(db);

  require('./workers/dailyWorker').start();
  require('./services/backupScheduler').start();

  app.listen(PORT, () => console.log(`Bill Tracker running at http://localhost:${PORT}`));
}

main().catch(err => { console.error('Startup failed:', err); process.exit(1); });
