import compression from "compression";
import cors from "cors";
import expressWs from "express-ws";
import express from "express";
import helmet from "helmet";
// hocuspocus server
import { getHocusPocusServer } from "@/core/hocuspocus-server.js";
// helpers
import { convertHTMLDocumentToAllFormats } from "@/core/helpers/convert-document.js";
import { logger, manualLogger } from "@/core/helpers/logger.js";
import { errorHandler } from "@/core/helpers/error-handler.js";
// types
import { TConvertDocumentRequestBody } from "@/core/types/common.js";

const app: any = express();
expressWs(app);

app.set("port", process.env.PORT || 3000);

// Security middleware
app.use(helmet());

// Middleware for response compression
app.use(
  compression({
    level: 6,
    threshold: 5 * 1000,
  })
);

// Logging middleware
app.use(logger);

// Body parsing middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// cors middleware
app.use(cors());

const router = express.Router();

const HocusPocusServer = await getHocusPocusServer().catch((err) => {
  manualLogger.error("Failed to initialize HocusPocusServer:", err);
  process.exit(1);
});

router.get("/health", (_req, res) => {
  res.status(200).json({ status: "OK" });
});

router.ws("/collaboration", (ws, req) => {
  try {
    HocusPocusServer.handleConnection(ws, req);
  } catch (err) {
    manualLogger.error("WebSocket connection error:", err);
    ws.close();
  }
});

router.post("/convert-document", (req, res) => {
  const { description_html, variant } = req.body as TConvertDocumentRequestBody;
  try {
    if (description_html === undefined || variant === undefined) {
      res.status(400).send({
        message: "Missing required fields",
      });
      return;
    }
    const { description, description_binary } = convertHTMLDocumentToAllFormats({
      document_html: description_html,
      variant,
    });
    res.status(200).json({
      description,
      description_binary,
    });
  } catch (error) {
    manualLogger.error("Error in /convert-document endpoint:", error);
    res.status(500).send({
      message: `Internal server error. ${error}`,
    });
  }
});

app.use(process.env.LIVE_BASE_PATH || "/live", router);

app.use((_req, res) => {
  res.status(404).send("Not Found");
});

app.use(errorHandler);

const liveServer = app.listen(app.get("port"), () => {
  manualLogger.info(`Plane Live server has started at port ${app.get("port")}`);
});

const gracefulShutdown = async () => {
  manualLogger.info("Starting graceful shutdown...");

  try {
    // Close the HocusPocus server WebSocket connections
    await HocusPocusServer.destroy();
    manualLogger.info("HocusPocus server WebSocket connections closed gracefully.");

    // Close the Express server
    liveServer.close(() => {
      manualLogger.info("Express server closed gracefully.");
      process.exit(1);
    });
  } catch (err) {
    manualLogger.error("Error during shutdown:", err);
    process.exit(1);
  }

  // Forcefully shut down after 10 seconds if not closed
  setTimeout(() => {
    manualLogger.error("Forcing shutdown...");
    process.exit(1);
  }, 10000);
};

// Graceful shutdown on unhandled rejection
process.on("unhandledRejection", (err: any) => {
  manualLogger.info("Unhandled Rejection: ", err);
  manualLogger.info(`UNHANDLED REJECTION! 💥 Shutting down...`);
  gracefulShutdown();
});

// Graceful shutdown on uncaught exception
process.on("uncaughtException", (err: any) => {
  manualLogger.info("Uncaught Exception: ", err);
  manualLogger.info(`UNCAUGHT EXCEPTION! 💥 Shutting down...`);
  gracefulShutdown();
});
