/**
 * server.js — точка входа приложения.
 *
 * Отвечает только за:
 *   1. Инициализацию Express и глобальных middleware (helmet, static, body-parser)
 *   2. Инициализацию auth (initAuth) и CSRF (initCsrf)
 *   3. Подключение роутеров из src/routes/ с передачей зависимостей
 *   4. Старт HTTP-сервера
 *
 * Бизнес-логика — в src/routes/*.js
 * DB-запросы   — в src/queries.js
 * Auth/JWT     — в src/auth.js
 * Валидация    — в src/validators.js
 * Конфиг/хелперы — в src/config.js
 */

'use strict';

const express = require('express');
const cookieParser = require('cookie-parser');
const path = require('path');
const helmet = require('helmet');
require('dotenv').config();

const { checkConnection }  = require('./src/db');
const { initAuth }         = require('./src/auth');
const q                    = require('./src/queries');
const { MOCK_USERS, APP_VERSION } = require('./src/config');
const { authLimiter }      = require('./src/middleware/rateLimit');
const { createSessionMiddleware } = require('./src/middleware/session');
const { aggregateCIDRs }   = require('./src/validators');
const { CSP_DIRECTIVES }   = require('./src/middleware/csp');
const { createUiRouter }   = require('./ui/index');

const app = express();
const PORT = process.env.PORT || 3000;

app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// Версия доступна во всех шаблонах как <%= appVersion %> без передачи вручную.
app.locals.appVersion = APP_VERSION;

// ── Безопасность HTTP-заголовков ───────────────────────────────────────────────
// helmet выставляет все security-заголовки одним вызовом, включая CSP.
// Директивы CSP вынесены в src/middleware/csp.js.
app.use(helmet({ contentSecurityPolicy: { directives: CSP_DIRECTIVES } }));

// За reverse-proxy (nginx/k8s ingress) — доверяем первому хопу для secure cookies.
if (process.env.NODE_ENV === 'production') app.set('trust proxy', 1);

app.use(express.static(path.join(__dirname, 'public')));
app.use(express.urlencoded({ extended: true, limit: '32kb' }));
app.use(cookieParser());

async function start() {
  const auth = await initAuth();

  // ── Сессии ────────────────────────────────────────────────────────────────
  const { pool } = require('./src/db');
  app.use(createSessionMiddleware(pool));

  // ── Fail-fast в production ────────────────────────────────────────────────
  // Если секреты не заданы — приложение в проде немедленно падает.
  // Дефолтные значения — только для локальной разработки.
  if (process.env.NODE_ENV === 'production') {
    const DEFAULT_SESSION = 'dev-session-secret-change-me';
    if (!process.env.SESSION_SECRET || process.env.SESSION_SECRET === DEFAULT_SESSION) {
      console.error('FATAL: SESSION_SECRET не задан или равен дефолту в production!');
      process.exit(1);
    }
  }

  // k8s liveness probe — простая проверка что сервер жив
  app.get('/healthz', (req, res) => res.send('OK'));

  // JWKS endpoint для валидации токенов внешними сервисами (только в mock-режиме)
  if (auth.jwksHandler) app.get('/.well-known/jwks.json', auth.jwksHandler);

  // ── /export — публичный (ВРЕМЕННО без авторизации) ───────────────────────
  // 🔮 КОГДА Keycloak SSO: заменить на app.use('/export', auth.bearerMiddleware, ...)
  app.get('/export',
    /* 🔮 auth.bearerMiddleware, */
    async (req, res) => {
      try {
        const cidrs = await q.getExportCIDRs();
        const aggregated = aggregateCIDRs(cidrs);
        const text = aggregated.join('\n') + (aggregated.length ? '\n' : '');
        res.set('Content-Type', 'text/plain; charset=utf-8');
        const fname = req.query.filename || 'white-list.txt';
        const disp  = req.query.view === '1' ? 'inline' : 'attachment';
        res.set('Content-Disposition', disp + '; filename="' + fname + '"');
        res.send(text);
      } catch (e) {
        console.error('[export] Error:', e);
        res.status(500).send('Ошибка экспорта');
      }
    }
  );

  // ── REST API v1 (Bearer JWT, без сессий/CSRF) ─────────────────────────────
  const { createApiRouter } = require('./src/api/index');
  app.use('/api/v1', createApiRouter({ auth, q }));

  // ── UI-слой: SSR через EJS, данные из /api/v1/* ───────────────────────────
  // Монтируется ПОСЛЕ /api/v1/ — не перехватывает API-запросы.
  // Хранит Bearer token в сессии, рендерит те же views/*.ejs.
  app.use('/', createUiRouter({ auth, MOCK_USERS, authLimiter }));

  // ── Общий обработчик ошибок ───────────────────────────────────────────────
  // eslint-disable-next-line no-unused-vars
  app.use((err, req, res, next) => {
    console.error('Unhandled error:', err);
    res.status(500).send('Внутренняя ошибка сервера');
  });

  // ── Старт сервера ─────────────────────────────────────────────────────────────
  checkConnection()
    .then(() => console.log('DB connected'))
    .catch(e => console.error('DB not ready:', e.message));

  return app;
}

// Запускаем сервер только если файл запущен напрямую (не через require)
if (require.main === module) {
  start()
    .then(a => a.listen(PORT, () => console.log('Server on port ' + PORT)))
    .catch(e => { console.error('Startup error:', e); process.exit(1); });
}

module.exports = { start };

