require("dotenv").config();
const express = require("express");
const couchdbService = require("./services/couchdbService");
const sseService = require("./services/sseService");
const cors = require("cors");
const http = require("http");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");
const mongoSanitize = require("express-mongo-sanitize");
const xss = require("xss-clean");
const { errorHandler } = require("./middleware/errorHandler");
const requestLogger = require("./middleware/requestLogger");
const logger = require("./utils/logger");
const { validateEnv, logEnvConfig } = require("./utils/validateEnv");

// Validate environment variables before starting server
try {
  validateEnv();
  logEnvConfig();
} catch (error) {
  logger.error('Environment validation failed:');
  logger.error(error.message);
  process.exit(1);
}

const app = express();
const server = http.createServer(app);
const port = process.env.PORT || 5000;

// Trust proxy - required when behind ingress/reverse proxy
app.set('trust proxy', 1);

// Security Headers - Helmet
app.use(helmet());

// CORS Configuration
app.use(
  cors({
    origin: process.env.FRONTEND_URL || "http://localhost:3000",
    credentials: true,
  }),
);

// Body Parser
app.use(express.json());

// Data Sanitization against NoSQL injection
app.use(mongoSanitize());

// Data Sanitization against XSS
app.use(xss());

// Request Logging
app.use(requestLogger);

// Rate Limiting for Auth Routes (5 requests per 15 minutes)
const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 requests per windowMs
  message: {
    success: false,
    error: "Too many authentication attempts, please try again later",
  },
  standardHeaders: true,
  legacyHeaders: false,
  // Trust proxy when behind ingress
  validate: { trustProxy: false },
});

// General API Rate Limiting (100 requests per 15 minutes)
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // 100 requests per windowMs
  message: {
    success: false,
    error: "Too many requests, please try again later",
  },
  standardHeaders: true,
  legacyHeaders: false,
  // Trust proxy when behind ingress
  validate: { trustProxy: false },
});

// Database Connection
// CouchDB (primary database)
// Skip initialization during testing
if (process.env.NODE_ENV !== 'test') {
  couchdbService.initialize()
    .then(() => logger.info("CouchDB initialized successfully"))
    .catch((err) => {
      logger.error("CouchDB initialization failed", err);
      process.exit(1); // Exit if CouchDB fails to initialize since it's the primary database
    });
}

// Make sse available to routes
app.set("sse", sseService);

// Routes
const authRoutes = require("./routes/auth");
const streetRoutes = require("./routes/streets");
const taskRoutes = require("./routes/tasks");
const postRoutes = require("./routes/posts");
const commentsRoutes = require("./routes/comments");
const eventRoutes = require("./routes/events");
const rewardRoutes = require("./routes/rewards");
const reportRoutes = require("./routes/reports");
const badgesRoutes = require("./routes/badges");
const aiRoutes = require("./routes/ai");
const paymentRoutes = require("./routes/payments");
const userRoutes = require("./routes/users");
const cacheRoutes = require("./routes/cache");
const profileRoutes = require("./routes/profile");
const analyticsRoutes = require("./routes/analytics");
const leaderboardRoutes = require("./routes/leaderboard");
const sseRoutes = require("./routes/sse");

// Apply rate limiters
app.use("/api/auth/register", authLimiter);
app.use("/api/auth/login", authLimiter);
app.use("/api", apiLimiter);

// Health check endpoint (for Kubernetes liveness/readiness probes)
app.get("/api/health", async (req, res) => {
  try {
    const couchdbStatus = await couchdbService.checkConnection();
    
    // Get SSE stats
    const sseStats = sseService.getStats();
    
    const isHealthy = couchdbStatus;
    
    res.status(isHealthy ? 200 : 503).json({
      status: isHealthy ? "healthy" : "degraded",
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      services: {
        couchdb: couchdbStatus ? "connected" : "disconnected",
        sse: {
          totalClients: sseStats.totalClients,
          totalTopics: sseStats.totalTopics
        }
      },
      memory: {
        heapUsed: Math.round(process.memoryUsage().heapUsed / 1024 / 1024) + " MB",
        heapTotal: Math.round(process.memoryUsage().heapTotal / 1024 / 1024) + " MB",
        rss: Math.round(process.memoryUsage().rss / 1024 / 1024) + " MB"
      }
    });
  } catch (error) {
    res.status(503).json({
      status: "unhealthy",
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      services: {
        couchdb: "disconnected",
        sse: "unknown"
      },
      error: error.message,
    });
  }
});

// Routes
app.use("/api/auth", authRoutes);
app.use("/api/streets", streetRoutes);
app.use("/api/tasks", taskRoutes);
app.use("/api/posts", postRoutes);
app.use("/api/posts", commentsRoutes); // Comments are nested under posts
app.use("/api/events", eventRoutes);
app.use("/api/rewards", rewardRoutes);
app.use("/api/reports", reportRoutes);
app.use("/api/badges", badgesRoutes);
app.use("/api/ai", aiRoutes);
app.use("/api/payments", paymentRoutes);
app.use("/api/users", userRoutes);
app.use("/api/cache", cacheRoutes);
app.use("/api/profile", profileRoutes);
app.use("/api/analytics", analyticsRoutes);
app.use("/api/leaderboard", leaderboardRoutes);
app.use("/api/sse", sseRoutes);

app.get("/", (req, res) => {
  res.send("Street Adoption App Backend");
});

// Error Handler Middleware (must be last)
app.use(errorHandler);

// Only start server if this file is run directly (not when required by tests)
if (require.main === module) {
  server.listen(port, () => {
    logger.info(`Server started`, { port, env: process.env.NODE_ENV || 'development' });
  });
}

// Export app and server for testing
module.exports = { app, server };

// Graceful shutdown
process.on("SIGTERM", async () => {
  logger.info("SIGTERM received, shutting down gracefully");
  
  try {
    // Close CouchDB connection
    await couchdbService.shutdown();
    logger.info("CouchDB connection closed");
    
    // Close server
    server.close(() => {
      logger.info("Server closed");
      process.exit(0);
    });
  } catch (error) {
    logger.error("Error during shutdown", error);
    process.exit(1);
  }
});

process.on("SIGINT", async () => {
  logger.info("SIGINT received, shutting down gracefully");
  
  try {
    // Close CouchDB connection
    await couchdbService.shutdown();
    logger.info("CouchDB connection closed");
    
    // Close server
    server.close(() => {
      logger.info("Server closed");
      process.exit(0);
    });
  } catch (error) {
    logger.error("Error during shutdown", error);
    process.exit(1);
  }
});
