const express = require("express");
const app = express();
const path = require("path");
require("dotenv").config();
const logger = require("morgan");
const cors = require("cors");
const session = require("express-session");
const cookieparser = require("cookie-parser");
const fs = require("fs");
const serialize = require("serialize-javascript");

const routes = require("./routes/router.js");
const swaggerUi = require("swagger-ui-express");
const swaggerDocument = require("./swagger-output.json");

const Setting = require("./models/setting.model");
const Seo = require("./services/seo.service");
const Cms = require("./services/cms.service");

const { getEnv } = require("./utils/envLoader");
const { generatedErrors } = require("./middlewares/error.js");
const ErrorHandler = require("./utils/ErrorHandler.js");
const {
  installerMiddleware,
} = require("./installer/middleware/installer.middleware");
const installerRoutes = require("./installer/routes/installer.routes");
const { globalLimiter } = require("./middlewares/rateLimiter");
const { detectLanguage } = require("./middlewares/language.middleware");

// ================= BASIC MIDDLEWARE =================

app.use(logger("tiny"));

app.use(
  cors({
    origin: [
      "http://localhost:5173",
      "http://localhost:3000",
      "http://localhost:3001",
      "http://localhost:5174",
    ],
    credentials: true,
  })
);

// Stripe webhook raw body
app.use(
  "/api/v1/payment/webhook/stripe",
  express.raw({ type: "application/json" })
);

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const baseDir = process.cwd();
// Serve uploads correctly
app.use("/uploads", express.static(path.join(baseDir, "public/uploads")));

app.use(
  session({
    resave: false,
    saveUninitialized: true,
    secret:
      getEnv("EXPRESS_SESSION_SECRET") ||
      process.env.EXPRESS_SESSION_SECRET ||
      "its_a_secret",
  })
);

app.use(cookieparser());

app.use(detectLanguage);
app.use(installerMiddleware);
app.use("/install", installerRoutes);
app.use("/api/v1", globalLimiter);

// ================= API =================

app.use("/api/v1", routes);

// ================= SWAGGER =================

app.use(
  "/api-docs",
  swaggerUi.serve,
  swaggerUi.setup(swaggerDocument, {
    explorer: true,
  })
);

// ================= FRONTEND STATIC PATHS =================

const driverDist = path.join(baseDir, "frontend/driver/dist");
const vendorDist = path.join(baseDir, "frontend/vendor/dist");
const zoneAdminDist = path.join(baseDir, "frontend/zone-admin/dist");
const superAdminDist = path.join(baseDir, "frontend/super-admin/dist");
const userDist = path.join(baseDir, "frontend/user/dist/client");

// SPA static
app.use("/driver", express.static(driverDist));
app.use("/vendor", express.static(vendorDist));
app.use("/zone-admin", express.static(zoneAdminDist));
app.use("/zone", express.static(zoneAdminDist)); // Fallback for old build paths
app.use("/super-admin", express.static(superAdminDist));

// SPA fallbacks
app.get(/^\/driver\/.*/, (req, res) => {
  res.sendFile(path.join(driverDist, "index.html"));
});

app.get(/^\/vendor\/.*/, (req, res) => {
  res.sendFile(path.join(vendorDist, "index.html"));
});

app.get(/^\/zone-admin\/.*/, (req, res) => {
  const indexPath = path.join(zoneAdminDist, "index.html");
  let html = fs.readFileSync(indexPath, "utf-8");

  // Fix asset paths: /zone/ -> /zone-admin/
  html = html.replace(/\/zone\//g, "/zone-admin/");

  res.set("Content-Type", "text/html").send(html);
});

app.get(/^\/super-admin\/.*/, (req, res) => {
  res.sendFile(path.join(superAdminDist, "index.html"));
});

// ================= SSR USER =================

async function renderSSR(req, res, next) {
  try {
    console.log(`[SSR] Starting render for: ${req.url}`);

    const templatePath = path.join(
      baseDir,
      "frontend/user/dist/client/index.html"
    );
    console.log("BaseDir:", baseDir);
    console.log("Template Path:", templatePath);
    const serverEntryPath = path.join(
      baseDir,
      "frontend/user/dist/server/entry-server.mjs"
    );

    // Check if files exist
    if (!fs.existsSync(templatePath)) {
      console.error("[SSR] User frontend template not found:", templatePath);
      return res
        .status(404)
        .send("User frontend not built. Please run the build command.");
    }

    if (!fs.existsSync(serverEntryPath)) {
      console.error(
        "[SSR] User frontend server entry not found:",
        serverEntryPath
      );
      const template = fs.readFileSync(templatePath, "utf-8");
      return res.status(200).set({ "Content-Type": "text/html" }).end(template);
    }

    console.log("[SSR] Loading template and server entry...");
    const template = fs.readFileSync(templatePath, "utf-8");

    // Convert Windows path to file URL for dynamic import
    const fileUrl = new URL(`file:///${serverEntryPath.replace(/\\/g, "/")}`);
    console.log("[SSR] Loading module from:", fileUrl.href);

    const serverModule = await import(fileUrl.href);
    const { render } = serverModule;

    console.log("[SSR] Fetching initial data...");
    const [settings, seoRoutes, banners] = await Promise.all([
      Setting.getSettingsWithDecryption(),
      Seo.getIntercityRoutes({ isActive: true }),
      Cms.getBanners(),
    ]);

    console.log("[SSR] Data fetched successfully");
    const initialData = {
      setting: { settings, loading: false },
      seo: { intercityRoutes: seoRoutes.routes, loading: false },
      cms: { banners, loading: false },
    };

    console.log("[SSR] Rendering HTML...");
    const { html, preloadedState } = render(req.url, initialData);

    const finalHtml = template
      .replace(`<!--app-html-->`, html)
      .replace(
        `<!--redux-state-->`,
        `<script>window.__PRELOADED_STATE__ = ${serialize(
          preloadedState
        )}</script>`
      );

    console.log("[SSR] Sending response");
    res.status(200).set({ "Content-Type": "text/html" }).end(finalHtml);
  } catch (error) {
    console.error("[SSR] Error occurred:", error.message);
    console.error("[SSR] Error stack:", error.stack);

    // Fallback to static HTML on error
    try {
      const templatePath = path.resolve(
        __dirname,
        "../frontend/user/dist/client/index.html"
      );
      if (fs.existsSync(templatePath)) {
        console.log("[SSR] Falling back to static HTML");
        const template = fs.readFileSync(templatePath, "utf-8");
        return res
          .status(200)
          .set({ "Content-Type": "text/html" })
          .end(template);
      }
    } catch (fallbackError) {
      console.error("[SSR] Fallback error:", fallbackError);
    }

    console.error("[SSR] All fallbacks failed, passing to error handler");
    next(error);
  }
}

// Catch-all for SSR (after SPA + API)
app.use(async (req, res, next) => {
  // Skip SSR for these paths
  if (
    req.url.startsWith("/api") ||
    req.url.startsWith("/driver") ||
    req.url.startsWith("/vendor") ||
    req.url.startsWith("/zone-admin") ||
    req.url.startsWith("/super-admin") ||
    req.url.startsWith("/uploads") ||
    req.url.startsWith("/api-docs") ||
    req.url.startsWith("/install") ||
    req.url.startsWith("/assets") || // Skip assets
    req.url.match(/\.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$/) // Skip static files
  ) {
    return next();
  }

  // Handle user frontend with SSR
  console.log(`[SSR] Handling request for: ${req.url}`);
  return renderSSR(req, res, next);
});

// ================= ERRORS =================

app.use(generatedErrors);

app.use((req, res, next) => {
  next(new ErrorHandler(`Requested URL not found ${req.url}`, 404));
});

module.exports = app;
