import http from "http";

import cors from "cors";
import express from "express";
import helmet from "helmet";

import { env } from "./config/env.js";
import { db } from "./lib/db.js";
import { HttpError } from "./lib/http-error.js";
import { alertsRouter } from "./modules/alerts/alerts.routes.js";
import { authRouter } from "./modules/auth/auth.routes.js";
import { ensureAdminUser } from "./modules/auth/auth.service.js";
import { devicesRouter } from "./modules/devices/devices.routes.js";
import { createTelemetryRouter } from "./modules/telemetry/telemetry.routes.js";
import { TelemetryService } from "./modules/telemetry/telemetry.service.js";
import { geocodingRouter } from "./modules/geocoding/geocoding.routes.js";
import { settingsRouter } from "./modules/settings/settings.routes.js";
import { ensureSettingsStorage } from "./modules/settings/settings.service.js";
import { usersRouter } from "./modules/users/users.routes.js";
import { vehiclesRouter } from "./modules/vehicles/vehicles.routes.js";
import { createTrackerTcpServer } from "./protocols/trackerking/tcpHandler.js";
import { createRealtimeServer } from "./realtime/socket.js";

const start = async () => {
  await db.query("select 1");
  await ensureSettingsStorage();
  await ensureAdminUser();

  const app = express();
  const httpServer = http.createServer(app);
  const io = createRealtimeServer(httpServer);
  const telemetryService = new TelemetryService(io);
  const tcpServer = createTrackerTcpServer(telemetryService);

  app.set("trust proxy", 1);
  app.use(
    cors({
      origin: [env.APP_BASE_URL],
      credentials: true,
    }),
  );
  app.use(helmet());
  app.use(express.json({ limit: "1mb" }));

  app.get("/health", (_request, response) => {
    response.json({
      ok: true,
      apiBaseUrl: env.API_BASE_URL,
      tcpPort: env.TCP_PORT,
      timestamp: new Date().toISOString(),
    });
  });

  app.use("/auth", authRouter);
  app.use("/alerts", alertsRouter);
  app.use("/users", usersRouter);
  app.use("/vehicles", vehiclesRouter);
  app.use("/devices", devicesRouter);
  app.use("/geocoding", geocodingRouter);
  app.use("/settings", settingsRouter);
  app.use("/telemetry", createTelemetryRouter(telemetryService));

  app.use((error: unknown, _request: express.Request, response: express.Response, _next: express.NextFunction) => {
    if (error instanceof HttpError) {
      return response.status(error.statusCode).json({ message: error.message });
    }

    console.error(error);
    return response.status(500).json({ message: "Internal server error" });
  });

  httpServer.listen(env.API_PORT, () => {
    console.log(`api listening on ${env.API_PORT}`);
  });

  tcpServer.listen(env.TCP_PORT, () => {
    console.log(`tcp listener on ${env.TCP_PORT}`);
  });

  const shutdown = async () => {
    tcpServer.close();
    io.close();
    httpServer.close();
    await db.end();
    process.exit(0);
  };

  process.on("SIGINT", () => void shutdown());
  process.on("SIGTERM", () => void shutdown());
};

void start().catch((error) => {
  console.error("failed to start api", error);
  process.exit(1);
});
