import http from "node:http";
import os from "node:os";
import { logger } from "@/core/logger";
import { DateTime } from "luxon";
import { createApp } from "./app";
import { ENV } from "./config";
import { tryConnectToDatabase } from "./config/database";
import { initModules } from "./core/helpers";
import { registerModules } from "./register-modules";

// Guardamos información del estado del servidor
export const currentState = {
  launchedAt: DateTime.now(),
  appPath: process.cwd(),
  host: ENV.HOST,
  port: ENV.PORT,
  environment: ENV.NODE_ENV,
  connections: {} as Record<string, any>,
};

// Manejo de cierre forzado del servidor (graceful shutdown)
const serverStop = (server: http.Server) => {
  const forceTimeout = 30000;

  return new Promise<void>((resolve, reject) => {
    logger.warn("⚡️ Shutting down server");

    setTimeout(() => {
      logger.error("Could not close connections in time, forcefully shutting down");
      resolve();
    }, forceTimeout).unref();

    server.close((err) => {
      if (err) {
        return reject(err);
      }
      logger.info("Closed out remaining connections.");
      logger.info("❎ Bye!");
      resolve();
    });
  });
};

// Manejo de errores al iniciar el servidor
const serverError = (error: NodeJS.ErrnoException) => {
  logger.error(`⛔️ Server wasn't able to start properly.`, {
    label: "serverError0",
    error,
  });

  if (error.code === "EADDRINUSE") {
    logger.error(error.message, { error, label: "serverError1" });
    //logger.error(`The port ${error.port} is already used by another application.`);
  } else {
    logger.error(error.message, { error, label: "serverError2" });
  }

  // Dependiendo de la criticidad, podrías forzar el proceso a salir
  process.exit(1);
};

// Almacena en "connections" cada nueva conexión (descomentar si se quiere seguimiento)
const serverConnection = (conn: any) => {
  const key = `${conn.remoteAddress}:${conn.remotePort}`;
  currentState.connections[key] = conn;

  conn.on("close", () => {
    delete currentState.connections[key];
  });
};

//const sequelizeConn = createSequelizeAdapter();
//const firebirdConn = createFirebirdAdapter();

// Cargar paquetes de la aplicación
registerModules();

const app = createApp();

// Crea el servidor HTTP
const server = http
  .createServer(app)
  .once("listening", () =>
    process.on("SIGINT", async () => {
      // Por ejemplo, podrías desconectar la base de datos aquí:
      // firebirdConn.disconnect();
      // O forzar desconexión en adapters
      // sequelizeConn.close();

      await serverStop(server);
    })
  )
  .on("close", () =>
    logger.info(`Shut down at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`)
  )
  .on("connection", serverConnection)
  .on("error", serverError);

// Ejemplo de adapters de base de datos (descoméntalos si los necesitas)
// const sequelizeConn = createSequelizeAdapter();
// const firebirdConn = createFirebirdAdapter();

// Manejo de promesas no capturadas
process.on("unhandledRejection", (reason: any, promise: Promise<any>) => {
  logger.error(`❌ Unhandled rejection at:", ${promise}, "reason:", ${reason}`);
  // Dependiendo de la aplicación, podrías desear una salida total o un cierre controlado
  process.exit(1);
});

// Manejo de excepciones no controladas
process.on("uncaughtException", (error: Error) => {
  // firebirdConn.disconnect();
  logger.error(`❌ Uncaught exception: ${error.message}`);
  logger.error(error.stack);
  // process.exit(1);
});

// Arranca el servidor si la conexión a la base de datos va bien
(async () => {
  try {
    const now = DateTime.now();
    logger.info(`Time: ${now.toLocaleString(DateTime.DATETIME_FULL)} ${now.zoneName}`);
    logger.info(`Launched in: ${now.diff(currentState.launchedAt).toMillis()} ms`);
    logger.info(`Environment: ${currentState.environment}`);
    logger.info(`Process PID: ${process.pid}`);

    const database = await tryConnectToDatabase();
    // Lógica de inicialización de DB, si procede:
    // initStructure(sequelizeConn.connection);
    // insertUsers();

    await initModules({ app, database, baseRoutePath: "/api/v1", logger });

    server.listen(currentState.port, () => {
      server.emit("listening");

      const networkInterfaces = os.networkInterfaces();
      const addresses: string[] = [];

      // Obtiene todas las direcciones IPv4
      for (const interfaceName in networkInterfaces) {
        const networkInterface = networkInterfaces[interfaceName];
        if (networkInterface) {
          for (const iface of networkInterface) {
            if (iface.family === "IPv4" && !iface.internal) {
              addresses.push(iface.address);
            }
          }
        }
      }

      for (const address in addresses) {
        logger.info(`⚡️ Server accessible at: http://${address}:${currentState.port}`);
      }

      logger.info(`Server started at: ${DateTime.now().toLocaleString(DateTime.DATETIME_FULL)}`);
      logger.info(`Server PID: ${process.pid}`);
      logger.info(
        `Server launched in: ${DateTime.now().diff(currentState.launchedAt).toMillis()} ms`
      );
      logger.info(`Server path: ${currentState.appPath}`);
      logger.info(`Server environment: ${currentState.environment}`);
      logger.info("To shut down your server, press <CTRL> + C at any time");
    });
  } catch (error) {
    serverError(error as NodeJS.ErrnoException);
  }
})();
