// Load environment-specific config
const env = process.env.NODE_ENV || "dev";
const envFile = `.env.${env}`;

require("dotenv").config({
  path: envFile,
});
const express = require("express");
const http = require("http");
const { Server } = require("socket.io");
const cors = require("cors");
const bodyParser = require("body-parser");
const path = require("path");
const helmet = require("helmet");
const { sequelize } = require("./models"); // Import from models/index.js to ensure associations are loaded
const { cookieParser } = require("./middleware/csrf");
const logger = require("./utils/logger");
const morgan = require("morgan");

const authRoutes = require("./routes/auth");
const { router: alphaRoutes } = require("./routes/alpha");
const userRoutes = require("./routes/users");
const itemRoutes = require("./routes/items");
const rentalRoutes = require("./routes/rentals");
const messageRoutes = require("./routes/messages");
const forumRoutes = require("./routes/forum");
const stripeRoutes = require("./routes/stripe");
const stripeWebhookRoutes = require("./routes/stripeWebhooks");
const mapsRoutes = require("./routes/maps");
const conditionCheckRoutes = require("./routes/conditionChecks");
const feedbackRoutes = require("./routes/feedback");
const uploadRoutes = require("./routes/upload");
const healthRoutes = require("./routes/health");

const emailServices = require("./services/email");
const s3Service = require("./services/s3Service");

// Socket.io setup
const { authenticateSocket } = require("./sockets/socketAuth");
const { initializeMessageSocket } = require("./sockets/messageSocket");

const app = express();
const server = http.createServer(app);

// Initialize Socket.io with CORS
const io = new Server(server, {
  cors: {
    origin: process.env.FRONTEND_URL || "http://localhost:3000",
    credentials: true,
    methods: ["GET", "POST"],
  },
});

// Apply socket authentication middleware
io.use(authenticateSocket);

// Initialize message socket handlers
initializeMessageSocket(io);

// Store io instance in app for use in routes
app.set("io", io);

// Import security middleware
const {
  enforceHTTPS,
  securityHeaders,
  addRequestId,
  sanitizeError,
} = require("./middleware/security");
const { generalLimiter } = require("./middleware/rateLimiter");
const errorLogger = require("./middleware/errorLogger");
const apiLogger = require("./middleware/apiLogger");
const { requireAlphaAccess } = require("./middleware/alphaAccess");

// Apply security middleware
app.use(enforceHTTPS);
app.use(addRequestId);
app.use(securityHeaders);

// Security headers with Helmet
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: ["'self'", "https://cdn.jsdelivr.net"],
        fontSrc: ["'self'"],
        scriptSrc: ["'self'", "https://accounts.google.com"],
        imgSrc: ["'self'"],
        connectSrc: ["'self'"],
        frameSrc: ["'self'", "https://accounts.google.com"],
      },
    },
  })
);

// Cookie parser for CSRF
app.use(cookieParser);

// HTTP request logging
app.use(morgan("combined", { stream: logger.stream }));

// API request/response logging
app.use("/api/", apiLogger);

// CORS with security settings (must come BEFORE rate limiter to ensure headers on all responses)
app.use(
  cors({
    origin: process.env.FRONTEND_URL || "http://localhost:3000",
    credentials: true,
    optionsSuccessStatus: 200,
    exposedHeaders: ["X-CSRF-Token"],
  })
);

// General rate limiting for all routes
app.use("/api/", generalLimiter);

// Body parsing with size limits
app.use(
  bodyParser.json({
    limit: "1mb",
    verify: (req, res, buf) => {
      // Store raw body for webhook verification
      req.rawBody = buf;
    },
  })
);
app.use(
  bodyParser.urlencoded({
    extended: true,
    limit: "1mb",
    parameterLimit: 100, // Limit number of parameters
  })
);

// Serve static files from uploads directory with CORS headers
app.use(
  "/uploads",
  helmet.crossOriginResourcePolicy({ policy: "cross-origin" }),
  express.static(path.join(__dirname, "uploads"))
);

// Health check endpoints (no auth, no rate limiting)
app.use("/health", healthRoutes);

// Stripe webhooks (no auth, uses signature verification instead)
app.use("/api/stripe/webhooks", stripeWebhookRoutes);

// Root endpoint
app.get("/", (req, res) => {
  res.json({ message: "Village Share API is running!" });
});

// Public routes (no alpha access required)
app.use("/api/alpha", alphaRoutes);
app.use("/api/auth", authRoutes); // Auth has its own alpha checks in registration

// Protected routes (require alpha access)
app.use("/api/users", requireAlphaAccess, userRoutes);
app.use("/api/items", requireAlphaAccess, itemRoutes);
app.use("/api/rentals", requireAlphaAccess, rentalRoutes);
app.use("/api/messages", requireAlphaAccess, messageRoutes);
app.use("/api/forum", requireAlphaAccess, forumRoutes);
app.use("/api/stripe", requireAlphaAccess, stripeRoutes);
app.use("/api/maps", requireAlphaAccess, mapsRoutes);
app.use("/api/condition-checks", requireAlphaAccess, conditionCheckRoutes);
app.use("/api/feedback", requireAlphaAccess, feedbackRoutes);
app.use("/api/upload", requireAlphaAccess, uploadRoutes);

// Error handling middleware (must be last)
app.use(errorLogger);
app.use(sanitizeError);

const PORT = process.env.PORT || 5000;

const { checkPendingMigrations } = require("./utils/checkMigrations");

sequelize
  .authenticate()
  .then(async () => {
    logger.info("Database connection established successfully");

    // Check for pending migrations
    const pendingMigrations = await checkPendingMigrations(sequelize);
    if (pendingMigrations.length > 0) {
      logger.error(
        `Found ${pendingMigrations.length} pending migration(s). Please run 'npm run db:migrate'`,
        { pendingMigrations }
      );
      process.exit(1);
    }
    logger.info("All migrations are up to date");

    // Initialize email services and load templates
    try {
      await emailServices.initialize();
      logger.info("Email services initialized successfully");
    } catch (err) {
      logger.error("Failed to initialize email services", {
        error: err.message,
        stack: err.stack,
      });
      // Fail fast - don't start server if email templates can't load
      if (env === "prod" || env === "production") {
        logger.error(
          "Cannot start server without email services in production"
        );
        process.exit(1);
      } else {
        logger.warn(
          "Email services failed to initialize - continuing in dev mode"
        );
      }
    }

    // Initialize S3 service for image uploads
    try {
      s3Service.initialize();
      logger.info("S3 service initialized successfully");
    } catch (err) {
      logger.error("Failed to initialize S3 service", {
        error: err.message,
        stack: err.stack,
      });
      logger.error("Cannot start server without S3 service in production");
      process.exit(1);
    }

    server.listen(PORT, () => {
      logger.info(`Server is running on port ${PORT}`, {
        port: PORT,
        environment: env,
      });
      logger.info("Socket.io server initialized");
    });
  })
  .catch((err) => {
    logger.error("Unable to connect to database", {
      error: err.message,
      stack: err.stack,
    });
    process.exit(1);
  });
