const express = require("express");
const session = require("express-session");
const pgSession = require("connect-pg-simple")(session);
const path = require("path");
const fs = require("fs");
const helmet = require("helmet");
const cors = require("cors");
const compressionMiddleware = require("./middleware/compression");
const { imageOptimization } = require("./middleware/imageOptimization");
const { pool, healthCheck } = require("./config/database");
const logger = require("./config/logger");
const { apiLimiter, authLimiter } = require("./config/rateLimiter");
const { errorHandler, notFoundHandler } = require("./middleware/errorHandler");
const {
  isDevelopment,
  getBaseDir,
  SESSION_CONFIG,
  BODY_PARSER_LIMITS,
} = require("./config/constants");
require("dotenv").config();

// SAFEGUARD: Register global process error handlers FIRST
require("./middleware/processHandlers");

const app = express();
const PORT = process.env.PORT || 5000;
const baseDir = getBaseDir();

logger.info(`📁 Serving from: ${baseDir}`);

// Start cache cleanup scheduler
const { startCleanup, stopCleanup } = require("./middleware/cache");
startCleanup();

// Compression middleware - should be early in the chain
app.use(compressionMiddleware);

// Security middleware
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        styleSrc: [
          "'self'",
          "'unsafe-inline'",
          "https://cdn.jsdelivr.net",
          "https://cdn.quilljs.com",
          "https://fonts.googleapis.com",
        ],
        scriptSrc: [
          "'self'",
          "'unsafe-inline'",
          "'unsafe-eval'",
          "https://cdn.jsdelivr.net",
          "https://cdn.quilljs.com",
        ],
        scriptSrcAttr: ["'self'", "'unsafe-inline'"],
        imgSrc: ["'self'", "data:", "blob:"],
        fontSrc: [
          "'self'",
          "https://cdn.jsdelivr.net",
          "https://fonts.gstatic.com",
        ],
        connectSrc: ["'self'", "https://cdn.jsdelivr.net"],
        objectSrc: ["'none'"],
        frameSrc: [
          "'self'",
          "https://www.google.com",
          "https://maps.google.com",
          "https://www.openstreetmap.org",
        ],
        upgradeInsecureRequests: !isDevelopment() ? [] : null,
      },
    },
    hsts: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true,
    },
    frameguard: { action: "sameorigin" },
    xssFilter: true,
    noSniff: true,
    referrerPolicy: { policy: "strict-origin-when-cross-origin" },
  }),
);

// CORS configuration
if (process.env.CORS_ORIGIN) {
  app.use(
    cors({
      origin: process.env.CORS_ORIGIN.split(","),
      credentials: true,
    }),
  );
}

// Trust proxy for rate limiting behind nginx
app.set("trust proxy", 1);

// Body parsers
app.use(express.json({ limit: BODY_PARSER_LIMITS.JSON }));
app.use(
  express.urlencoded({ extended: true, limit: BODY_PARSER_LIMITS.URLENCODED }),
);

// Fallback middleware for missing product images
const productImageFallback = (req, res, next) => {
  const imagePath = path.join(
    baseDir,
    "assets",
    "images",
    "products",
    req.path,
  );

  if (fs.existsSync(imagePath)) {
    return next();
  }

  const placeholderPath = path.join(
    baseDir,
    "assets",
    "images",
    "products",
    "placeholder.jpg",
  );
  logger.debug("Serving placeholder image", { requested: req.path });
  res.sendFile(placeholderPath);
};

app.use("/assets/images/products", productImageFallback);

// Root redirect - serve the original HTML site
app.get("/", (req, res) => {
  res.sendFile(path.join(baseDir, "public", "home.html"));
});

// Redirect /index to /home
app.get("/index", (req, res) => {
  res.redirect("/home");
});

app.use(
  express.static(path.join(baseDir, "public"), {
    index: false,
    etag: true,
    lastModified: true,
    setHeaders: (res, filepath) => {
      // Short cache for CSS/JS files (use cache busting for updates)
      if (filepath.endsWith(".css") || filepath.endsWith(".js")) {
        res.setHeader("Cache-Control", "public, max-age=300"); // 5 minutes
      } else if (filepath.endsWith(".html")) {
        res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
      } else {
        res.setHeader("Cache-Control", "public, max-age=86400"); // 1 day default
      }
    },
  }),
);
app.use(
  "/assets",
  express.static(path.join(baseDir, "public", "assets"), {
    etag: true,
    lastModified: true,
    setHeaders: (res, filepath) => {
      // Very short cache for CSS/JS to see changes quickly (with cache busting)
      if (filepath.endsWith(".css") || filepath.endsWith(".js")) {
        res.setHeader("Cache-Control", "public, max-age=300"); // 5 minutes
      } else if (
        filepath.endsWith(".woff2") ||
        filepath.endsWith(".woff") ||
        filepath.endsWith(".ttf")
      ) {
        res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
        res.setHeader("Access-Control-Allow-Origin", "*");
      } else {
        res.setHeader("Cache-Control", "public, max-age=86400"); // 1 day for images
      }
    },
  }),
);
// Optimized image serving with aggressive caching
app.use("/uploads", imageOptimization(path.join(baseDir, "uploads")));
app.use(
  "/uploads",
  express.static(path.join(baseDir, "uploads"), {
    maxAge: "365d", // Cache uploads for 1 year
    etag: true,
    lastModified: true,
    immutable: true,
  }),
);

// Session middleware
// SECURITY: Ensure SESSION_SECRET is set - fail fast if missing
if (
  !process.env.SESSION_SECRET ||
  process.env.SESSION_SECRET === "change-this-secret"
) {
  if (!isDevelopment()) {
    logger.error(
      "CRITICAL: SESSION_SECRET environment variable must be set in production!",
    );
    process.exit(1);
  }
  logger.warn(
    "WARNING: Using insecure session secret. Set SESSION_SECRET in production!",
  );
}

app.use(
  session({
    store: new pgSession({
      pool: pool,
      tableName: "session",
      createTableIfMissing: true,
    }),
    secret:
      process.env.SESSION_SECRET ||
      (isDevelopment() ? "dev-secret-change-in-production" : ""),
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: !isDevelopment(),
      httpOnly: true,
      maxAge: SESSION_CONFIG.COOKIE_MAX_AGE,
      sameSite: isDevelopment() ? "lax" : "strict",
    },
    proxy: !isDevelopment(),
    name: SESSION_CONFIG.SESSION_NAME,
    rolling: true, // Reset session expiration on each request
  }),
);

// Request logging
app.use((req, res, next) => {
  logger.info("Request received", {
    method: req.method,
    path: req.path,
    ip: req.ip,
  });
  next();
});

app.use((req, res, next) => {
  res.locals.session = req.session;
  res.locals.currentPath = req.path;
  next();
});

// API Routes
const authRoutes = require("./routes/auth");
const adminRoutes = require("./routes/admin");
const publicRoutes = require("./routes/public");
const usersRoutes = require("./routes/users");
const uploadRoutes = require("./routes/upload");
const customerAuthRoutes = require("./routes/customer-auth");
const customerCartRoutes = require("./routes/customer-cart");
const contactNewsletterRoutes = require("./routes/contact-newsletter");

// Admin redirect - handle /admin to redirect to login (must be before static files)
app.get("/admin", (req, res) => {
  res.redirect("/admin/login");
});

app.get("/admin/", (req, res) => {
  res.redirect("/admin/login");
});

// URL Rewriting Middleware - Remove .html extension (must be before static files)
app.use((req, res, next) => {
  // Skip API routes, static assets with extensions (except .html)
  if (
    req.path.startsWith("/api/") ||
    req.path.startsWith("/uploads/") ||
    req.path.startsWith("/assets/") ||
    (req.path.includes(".") && !req.path.endsWith(".html"))
  ) {
    return next();
  }

  // Check if path is for admin area
  if (req.path.startsWith("/admin/")) {
    const cleanPath = req.path.replace(/\.html$/, "").replace(/^\/admin\//, "");
    const htmlPath = path.join(baseDir, "admin", cleanPath + ".html");

    if (fs.existsSync(htmlPath)) {
      return res.sendFile(htmlPath);
    }
  }

  // Handle dynamic product pages: /product/:slug -> product.html
  if (req.path.startsWith("/product/")) {
    const productHtmlPath = path.join(baseDir, "public", "product.html");
    if (fs.existsSync(productHtmlPath)) {
      return res.sendFile(productHtmlPath);
    }
  }

  // Check if path is for public pages (root level pages)
  if (!req.path.includes("/admin/")) {
    let cleanPath = req.path.replace(/^\//, "").replace(/\.html$/, "");

    // Handle root path
    if (cleanPath === "" || cleanPath === "index") {
      cleanPath = "home";
    }

    const htmlPath = path.join(baseDir, "public", cleanPath + ".html");

    if (fs.existsSync(htmlPath)) {
      return res.sendFile(htmlPath);
    }
  }

  next();
});

// Apply rate limiting to API routes
app.use("/api/admin/login", authLimiter);
app.use("/api/admin/logout", authLimiter);
app.use("/api/customers/login", authLimiter);
app.use("/api/customers/signup", authLimiter);
app.use("/api", apiLimiter);

// API Routes
app.use("/api/admin", authRoutes);
app.use("/api/admin", adminRoutes);
app.use("/api/admin/users", usersRoutes);
app.use("/api/admin", uploadRoutes);
app.use("/api/customers", customerAuthRoutes);
app.use("/api/customers", customerCartRoutes);
app.use("/api", contactNewsletterRoutes);
app.use("/api", publicRoutes);

// Admin static files (must be after URL rewriting)
app.use(
  "/admin",
  express.static(path.join(baseDir, "admin"), {
    maxAge: "1d",
    etag: true,
    lastModified: true,
  }),
);

// Favicon route
app.get("/favicon.ico", (req, res) => {
  res.sendFile(path.join(baseDir, "public", "favicon.svg"));
});

// Old site (if needed for reference)
app.get("/old", (req, res) => {
  res.sendFile(path.join(baseDir, "public", "index.html"));
});

// Health check endpoint
const { CRITICAL_IMAGES } = require("./config/constants");

app.get("/health", async (req, res) => {
  try {
    const dbHealth = await healthCheck();
    const missingImages = CRITICAL_IMAGES.filter(
      (img) => !fs.existsSync(path.join(baseDir, img)),
    );

    const assetsHealthy = missingImages.length === 0;
    const overallHealthy = dbHealth.healthy && assetsHealthy;
    const status = overallHealthy ? 200 : 503;

    res.status(status).json({
      status: overallHealthy ? "ok" : "degraded",
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      database: dbHealth,
      assets: {
        healthy: assetsHealthy,
        missingCritical: missingImages,
      },
      memory: {
        used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
        total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
      },
    });
  } catch (error) {
    logger.error("Health check failed:", error);
    res.status(503).json({
      status: "error",
      timestamp: new Date().toISOString(),
      error: "Health check failed",
    });
  }
});

// 404 handler
app.use(notFoundHandler);

// Global error handler
app.use(errorHandler);

const server = app.listen(PORT, "0.0.0.0", () => {
  logger.info("========================================");
  logger.info("  SkyArtShop Backend Server");
  logger.info("========================================");
  logger.info(`🚀 Server running on http://localhost:${PORT}`);
  logger.info(`📦 Environment: ${process.env.NODE_ENV || "development"}`);
  logger.info(`🗄️  Database: PostgreSQL (${process.env.DB_NAME})`);
  logger.info("========================================");
});

// Graceful shutdown
const gracefulShutdown = (signal) => {
  logger.info(`${signal} received, shutting down gracefully...`);

  // Stop cache cleanup
  stopCleanup();

  server.close(() => {
    logger.info("HTTP server closed");

    pool.end(() => {
      logger.info("Database pool closed");
      process.exit(0);
    });
  });

  // Force close after 10 seconds
  setTimeout(() => {
    logger.error("Forced shutdown after timeout");
    process.exit(1);
  }, 10000);
};

process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
process.on("SIGINT", () => gracefulShutdown("SIGINT"));

process.on("unhandledRejection", (reason, promise) => {
  logger.error("Unhandled Rejection at:", { promise, reason });
});

process.on("uncaughtException", (error) => {
  logger.error("Uncaught Exception:", error);
  gracefulShutdown("UNCAUGHT_EXCEPTION");
});
