
const express = require('express');
const cors = require('cors');
const app = express();
const passport = require('passport');
const path = require('path');
const fs = require('fs');
const bodyParser = require('body-parser');
const helmet = require('helmet');
const config = require('./config');

const authRoutes = require('./routes/auth');
const fileRoutes = require('./routes/file');
const searchRoutes = require('./routes/search');
const pexelsRoutes = require('./routes/pexels');

const openaiRoutes = require('./routes/openai');
const coachingRoutes = require('./routes/coaching');
const coachingPublicRoutes = require('./routes/coachingPublic');
const { requireStaff } = require('./security/coachingAuthorization');
const { enforceReadOnlyDemo } = require('./security/readOnlyDemo');
const { createRateLimit } = require('./security/rateLimit');
const { protectCookieMutations } = require('./security/cookieAuth');
const { startCoachingScheduler } = require('./jobs/coachingScheduler');
const { initMonitoring } = require('./security/monitoring');
const crypto = require('crypto');
initMonitoring();


const usersRoutes = require('./routes/users');

const rolesRoutes = require('./routes/roles');

const permissionsRoutes = require('./routes/permissions');

const getBaseUrl = (url) => {
  if (!url) return '';
  return url.endsWith('/api') ? url.slice(0, -4) : url;
};

const options = {
  definition: {
    openapi: "3.0.0",
      info: {
        version: "1.0.0",
        title: "Coaching SaaS Workspace",
        description: "Coaching SaaS Workspace REST API for clients, session memory, resources, and coach operations.",
      },
    servers: [
      {
        url: getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || config.swaggerUrl,
        description: "Development server",
      }
    ],
    components: {
      securitySchemes: {
        bearerAuth: {
          type: 'http',
          scheme: 'bearer',
          bearerFormat: 'JWT',
        }
      },
      responses: {
        UnauthorizedError: {
          description: "Access token is missing or invalid"
        }
      }
    },
    security: [{
      bearerAuth: []
    }]
  },
  apis: ["./src/routes/*.js"],
};

if (process.env.NODE_ENV !== 'production') {
  const swaggerUI = require('swagger-ui-express');
  const swaggerJsDoc = require('swagger-jsdoc');
  const specs = swaggerJsDoc(options);
  app.use('/api-docs', function (req, res, next) {
    swaggerUI.host = getBaseUrl(process.env.NEXT_PUBLIC_BACK_API) || req.get('host');
    next()
  }, swaggerUI.serve, swaggerUI.setup(specs));
}

app.disable('x-powered-by');
app.use((req, res, next) => {
  req.requestId = req.get('x-request-id') || crypto.randomUUID();
  res.setHeader('X-Request-ID', req.requestId);
  next();
});
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'none'"],
      frameAncestors: ["'none'"],
    },
  },
  referrerPolicy: { policy: 'no-referrer' },
}));
app.use((req, res, next) => {
  const protocol = String(req.get('x-forwarded-proto') || req.protocol).split(',')[0].trim();
  const host = String(req.get('x-forwarded-host') || req.get('host')).split(',')[0].trim();
  const expectedOrigin = `${protocol}://${host}`;
  cors({
    credentials: true,
    origin(origin, callback) {
      if (!origin || origin === expectedOrigin) {
        callback(null, true);
        return;
      }

      const error = new Error('cors_origin_denied');
      error.code = 403;
      callback(error);
    },
  })(req, res, next);
});
require('./auth/auth');

app.use(bodyParser.json());
app.use(enforceReadOnlyDemo);
app.use(protectCookieMutations);

const authRateLimit = createRateLimit({ windowMs: 15 * 60 * 1000, max: 30, error: 'auth_rate_limit' });
const intakeRateLimit = createRateLimit({ windowMs: 60 * 60 * 1000, max: 10, error: 'intake_rate_limit' });
const costlyRateLimit = createRateLimit({ windowMs: 60 * 60 * 1000, max: 20, error: 'processing_rate_limit' });
const uploadRateLimit = createRateLimit({ windowMs: 60 * 60 * 1000, max: 30, error: 'upload_rate_limit' });

app.post('/api/auth/signin/local', authRateLimit);
app.post('/api/auth/signin/demo', authRateLimit);
app.post('/api/auth/send-password-reset-email', authRateLimit);
app.put('/api/auth/password-reset', authRateLimit);
app.use('/api/auth', authRoutes);
app.post('/api/file/upload/:table/:field', uploadRateLimit);
app.use('/api/file', fileRoutes);
app.use('/api/pexels', passport.authenticate('jwt', {session: false}), requireStaff, pexelsRoutes);
app.post('/api/coaching-public/intake', intakeRateLimit);
app.use('/api/coaching-public', coachingPublicRoutes);
app.enable('trust proxy');


app.use('/api/users', passport.authenticate('jwt', {session: false}), usersRoutes);

app.use('/api/roles', passport.authenticate('jwt', {session: false}), rolesRoutes);

app.use('/api/permissions', passport.authenticate('jwt', {session: false}), permissionsRoutes);

app.post('/api/coaching/session-memory/transcribe', costlyRateLimit);
app.post('/api/coaching/session-memory/generate', costlyRateLimit);
app.use('/api/coaching', passport.authenticate('jwt', {session: false}), coachingRoutes);
app.use(
    '/api/openai',
    passport.authenticate('jwt', { session: false }),
    requireStaff,
    openaiRoutes,
);
app.use(
    '/api/ai',
    passport.authenticate('jwt', { session: false }),
    requireStaff,
    openaiRoutes,
);

app.use(
  '/api/search',
  passport.authenticate('jwt', { session: false }),
  searchRoutes);

app.use(require('./helpers').commonErrorHandler);
  
const publicDir = path.join(
  __dirname,
  '../public',
);

if (fs.existsSync(publicDir)) {
  app.use('/', express.static(publicDir));

  app.get('*', function(request, response) {
    response.sendFile(
      path.resolve(publicDir, 'index.html'),
    );
  });
}

const PORT = Number(process.env.PORT || config.port || 3000);
if (!Number.isInteger(PORT) || PORT < 1 || PORT > 65535) {
  throw new Error(`Invalid backend port: ${process.env.PORT}`);
}

  app.listen(PORT, () => {
    console.log(`Listening on port ${PORT}`);
    startCoachingScheduler();
  });

module.exports = app;
