/**
 * Express application factory — imported by both server/index.js (production)
 * and the test suite. Keeping app creation separate from app.listen() means
 * tests can import a fresh instance without starting a real server or
 * triggering the side-effects in index.js (log files, process.exit, poller).
 */

const express = require('express');
const cookieParser = require('cookie-parser');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const crypto = require('crypto');

const sabnzbdRoutes = require('./routes/sabnzbd');
const sonarrRoutes = require('./routes/sonarr');
const radarrRoutes = require('./routes/radarr');
const embyRoutes = require('./routes/emby');
const dashboardRoutes = require('./routes/dashboard');
const historyRoutes = require('./routes/history');
const authRoutes = require('./routes/auth');
const verifyCsrf = require('./middleware/verifyCsrf');

function createApp({ skipRateLimits = false } = {}) {
  const app = express();

  if (process.env.TRUST_PROXY) {
    const trustValue = /^\d+$/.test(process.env.TRUST_PROXY)
      ? parseInt(process.env.TRUST_PROXY, 10)
      : process.env.TRUST_PROXY;
    app.set('trust proxy', trustValue);
  }

  // Per-request CSP nonce
  app.use((req, res, next) => {
    res.locals.cspNonce = crypto.randomBytes(16).toString('base64');
    next();
  });

  app.use((req, res, next) => {
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          scriptSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
          styleSrc: ["'self'", (req, res) => `'nonce-${res.locals.cspNonce}'`],
          styleSrcAttr: ["'unsafe-inline'"],
          imgSrc: ["'self'", 'data:', 'blob:'],
          fontSrc: ["'self'", 'data:'],
          connectSrc: ["'self'"],
          objectSrc: ["'none'"],
          baseUri: ["'self'"],
          frameAncestors: ["'none'"],
          formAction: ["'self'"],
          upgradeInsecureRequests: process.env.TRUST_PROXY ? [] : null
        }
      },
      hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
      referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
      crossOriginEmbedderPolicy: false
    })(req, res, next);
  });

  app.use((req, res, next) => {
    res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=()');
    next();
  });

  const apiLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: skipRateLimits ? Number.MAX_SAFE_INTEGER : 300,
    standardHeaders: true,
    legacyHeaders: false,
    message: { error: 'Too many requests, please try again later' }
  });

  app.use(cookieParser(process.env.COOKIE_SECRET || undefined));
  app.use(express.json({ limit: '64kb' }));

  // Health / readiness (no auth, no rate-limit)
  app.get('/health', (req, res) => {
    res.json({ status: 'ok', uptime: process.uptime() });
  });

  app.get('/ready', (req, res) => {
    const ready = !!(process.env.EMBY_URL);
    if (ready) {
      res.json({ status: 'ready' });
    } else {
      res.status(503).json({ status: 'not ready', reason: 'EMBY_URL not configured' });
    }
  });

  // API routes
  app.use('/api', apiLimiter);
  app.use('/api/auth', authRoutes);

  // CSRF protection for all state-changing API requests below
  app.use('/api', verifyCsrf);
  app.use('/api/sabnzbd', sabnzbdRoutes);
  app.use('/api/sonarr', sonarrRoutes);
  app.use('/api/radarr', radarrRoutes);
  app.use('/api/emby', embyRoutes);
  app.use('/api/dashboard', dashboardRoutes);
  app.use('/api/history', historyRoutes);

  // Global error handler
  // eslint-disable-next-line no-unused-vars
  app.use((err, req, res, next) => {
    console.error('[Server] Unhandled error:', err.message);
    res.status(500).json({ error: 'Internal server error' });
  });

  return app;
}

module.exports = { createApp };
