import Hapi from "@hapi/hapi";
import Boom from "@hapi/boom";
import Joi from "joi";
import dotenv from "dotenv";
dotenv.config({ encoding: "utf8" });
import * as auth_jwt from "hapi-auth-jwt2";
import { swaggerPlugin } from "./plugins/swagger";
import { registerApiRoutes } from "./plugins/routes";
import { registerI18nPlugin } from "./plugins/i18n";
import * as Sequelize from "sequelize";
const Op = Sequelize.Op;
import { sequelize } from "./api/config/sequelize";
import Models from "./api/models";
import { AppError } from "./utils/errors";
import { Token } from "./utils/token";
import requestIp from "request-ip";
import useragent from "useragent";
import AppCache from "./utils/appCache";
import { Common } from "./utils/common";
import { SessionService } from "./api/services/session.service";
import { io as clientIO } from "socket.io-client";
import { startScheduler } from "./plugins/scheduler";

export const startServer = async (server: Hapi.Server) => {
  try {
    let mode: boolean = false;
    let alter: boolean = true;
    if (process.env.APPLICATION_ENV == "TEST") {
      mode = true;
      alter = true;
    }
    if (+process.env.SYNCDB! || process.env.APPLICATION_ENV == "TEST") {
      await sequelize.sync({ force: mode, alter: alter });
    } else if (+process.env.SYNCDB!) {
      await sequelize.sync({ alter: alter });
    } else {
      await sequelize.authenticate();
    }
    console.log(`Listening on ${server.settings.host}:${server.settings.port}`);
    //await Model.Socket.truncate({cascade: false , restartIdentity:true,force: true});
    await Models.User.update(
      { onlineStatus: 0 },
      { where: { onlineStatus: 1, id: { [Op.gt]: 0 } } },
    );
    await SessionService.initiateSession(server);
    await Common.initializeAppCache(false);
    // globalThis.io = new SocketIOServer(server.listener, { cors: { origin: '*', } });
    globalThis.io = clientIO(process.env.SOCKET_SERVER!, {
      transports: ["websocket"],
    });
    // Register socket events
    io.on("updateCache", async (payload: any) => {
      console.log("updateCache..", payload);
      if (payload.value) AppCache.set(payload.index, payload.value);
      else AppCache.del(payload.index);
      return;
    });
  } catch (err) {
    console.log(err);
  }
};

export const init = async () => {
  const server = Hapi.server({
    port: process.env.APPLICATION_PORT! || 3000,
    host: process.env.APPLICATION_HOST! || "localhost",
    routes: {
      cors: {
        origin: ["*"],
        credentials: true,
        additionalHeaders: [
          "language",
          "timezone",
          "latitude",
          "longitude",
          "apikey",
          "connection",
          "x-csrf-token",
          "account",
        ],
        additionalExposedHeaders: ["Content-Disposition"],
      },
      validate: {
        options: {
          abortEarly: false,
        },
      },
    },
  });
  server.validator(Joi);
  await server.register(auth_jwt);
  server.auth.strategy("jwt", "jwt", {
    complete: true,
    verify: {
      aud: false,
      iss: false,
      sub: false,
      nbf: true,
      exp: true,
      maxAgeSec: 14400,
      timeSkewSec: 15,
    },
    key: process.env.JWT_PRIVATE_KEY!, // secret key
    validate: Token.validateToken, // validate function defined in common function for timestamp check
  });
  server.auth.default("jwt");
  await registerApiRoutes(server, process.env.ROUTE_PREFIX! || "");
  // Register Swagger plugin
  await registerI18nPlugin(server);
  await server.register(swaggerPlugin);
  // This runs only once right after the server starts
  // Set a variable before route handlers
  server.ext("onPostAuth", (request, h) => {
    if (+process.env.REQUEST_ENCRYPTION_FLAG!) {
      if (request.query && request.query.request) {
        const decryptedQuery = JSON.parse(
          Common.descrptRequest(request.query.request),
        );
        Object.assign(request.query, decryptedQuery);
      }
      if (
        request.payload &&
        typeof request.payload === "object" &&
        "payload" in request.payload
      ) {
        const encrypted = (request.payload as any).payload as string;
        const decryptedPayload = JSON.parse(Common.descrptRequest(encrypted));
        const parsed =
          typeof decryptedPayload === "string"
            ? JSON.parse(decryptedPayload)
            : decryptedPayload;
        Object.keys(request.payload).forEach(
          (k) => delete (request.payload as any)[k],
        );
        Object.assign(request.payload as any, parsed);
      }
    }
    return h.continue;
  });
  server.ext("onPreHandler", (request, h) => {
    request.app.clientIp = requestIp.getClientIp(request) || "";
    const ua = request.headers["user-agent"] || "";
    const agent = useragent.parse(ua);
    const deviceInfo = {
      browser: {
        name: agent.family, // e.g., "Chrome"
        version: agent.toVersion(), // e.g., "138.0.0"
      },
      os: {
        name: agent.os.family, // e.g., "Windows"
        version: agent.os.toVersion(), // e.g., "10.0.0"
      },
      device: {
        name: agent.device.family, // e.g., "Other"
        version: agent.device.toVersion(), // e.g., "0.0.0"
      },
    };

    request.app.deviceInfo = deviceInfo;
    return h.continue;
  });
  server.ext("onPreResponse", (request, h) => {
    try {
      const response = request.response;
      if (response instanceof AppError) {
        return h
          .response(response.toJSON()) // <- This ensures clean JSON
          .code(response.statusCode);
      }
      // 🔹 Only encrypt if it's a normal response with `.source`
      if (+process.env.REQUEST_ENCRYPTION_FLAG!) {
        if (response && typeof (response as any).source !== "undefined") {
          const source = (response as any).source;
          if (
            source &&
            "responseData" in source &&
            source.responseData !== null
          ) {
            source.responseData = Common.encryptResponse(
              JSON.stringify(source.responseData),
            );
            //console.log(JSON.parse(Common.descrptRequest(source.responseData)));
          }
        }
      }
      // If the response is an instance of AppError
      console.log("===>", (response as any).source ? JSON.stringify((response as any).source) : response);
      return h.continue;
    } catch (err) {
      console.log(err);
      return h.continue;
    }
  });
  process.on("unhandledRejection", (reason, promise) => {
    console.error("Unhandled Promise Rejection:", reason);
    // Optionally exit process or notify error monitoring
  });
  await server.start();
  AppCache.init();
  console.log("start");
  await startServer(server);
  await startScheduler();
  console.log(`🚀 Server is running at: ${server.info.uri}`);
  //console.log("Startup memory:", process.memoryUsage());
};
init();
