import Fastify, { type FastifyError, type FastifyInstance } from "fastify"
import cors from "@fastify/cors"
import { serializerCompiler, validatorCompiler } from "@fastify/type-provider-zod"
import { monitorEventLoopDelay } from "node:perf_hooks"
import { env } from "./config.js"
import authPlugin, { requireAuth } from "./plugins/auth.js"
import serversRoutes from "./routes/servers.js"
import bgpRoutes from "./routes/bgp.js"
import ospfRoutes from "./routes/ospf.js"
import execRoutes from "./routes/exec.js"
import filtersRoutes from "./routes/filters.js"
import recursiveRoutes from "./routes/recursive-routes.js"
import trafficRoutes from "./routes/traffic.js"
import trafficFlowRoutes from "./routes/traffic-flow.js"
import serversApiPingRoutes from "./routes/servers-api-ping.js"
import uptimeRoutes from "./routes/uptime.js"
import networkRoutes from "./routes/network.js"
import internetPathRoutes from "./routes/internet-path.js"
import evobgpRoutes from "./routes/evobgp.js"
import probesRoutes from "./routes/probes.js"
import schedulerRoutes from "./routes/scheduler.js"
import sidebarCountsRoutes from "./routes/sidebar-counts.js"
import alertsRoutes from "./routes/alerts.js"
import backupsRoutes from "./routes/backups.js"
import certificatesRoutes from "./routes/certificates.js"
import systemDatabaseRoutes from "./routes/system-database.js"
import eventsRoutes from "./routes/events.js"
import wireguardRoutes from "./routes/wireguard.js"
import firewallRoutes from "./routes/firewall.js"
import usersRoutes from "./routes/users.js"
import { refreshScheduler, stopScheduler } from "./services/scheduler.js"
import { getFlowWorkerHealth, startTrafficFlowListener, stopTrafficFlowListener } from "./services/traffic-flow-ingest.js"

const eventLoopDelay = monitorEventLoopDelay({ resolution: 20 })
eventLoopDelay.enable()

export async function buildApp(opts?: {
  logger?: boolean
  startScheduler?: boolean
}): Promise<FastifyInstance> {
  const usePrettyLogger =
    opts?.logger !== false && process.env.NODE_ENV !== "production"
  const app = Fastify({
    bodyLimit: 2 * 1024 * 1024,
    requestTimeout: 10 * 60 * 1000,
    logger:
      opts?.logger === false
        ? false
        : usePrettyLogger
          ? {
              transport: {
                target: "pino-pretty",
                options: {
                  colorize: true,
                  translateTime: "HH:MM:ss",
                  ignore: "pid,hostname",
                },
              },
            }
          : true,
  })

  app.setValidatorCompiler(validatorCompiler)
  app.setSerializerCompiler(serializerCompiler)

  app.setErrorHandler((error: FastifyError, request, reply) => {
    const status = typeof error.statusCode === "number" && error.statusCode >= 400
      ? error.statusCode
      : 500
    if (status >= 500) {
      request.log.error(error)
      return reply.status(status).send({ error: "Внутренняя ошибка сервера" })
    }
    const message = error instanceof Error ? error.message : "Ошибка запроса"
    return reply.status(status).send({ error: message })
  })

  await app.register(cors, {
    origin: env.CORS_ORIGIN,
    methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
  })

  await app.register(authPlugin)

  app.get("/health", async () => ({
    status: "ok",
    timestamp: new Date().toISOString(),
    version: process.env.APP_VERSION ?? "dev",
    eventLoopDelayMs: Math.round(eventLoopDelay.mean / 1e6),
    flowWorker: getFlowWorkerHealth(),
  }))

  app.get("/api/auth/config", async () => ({
    required: env.authRequired,
    portal_url: env.authPortalUrl,
    issuer: env.authIssuer,
  }))

  if (env.authRequired) {
    app.addHook("preHandler", async (request, reply) => {
      const pathname = request.url.split("?")[0] ?? request.url
      if (!pathname.startsWith("/api/")) return
      if (pathname === "/api/auth/config") return
      await requireAuth(request, reply)
      if (reply.sent) return
    })
  }

  await app.register(serversRoutes, { prefix: "/api/servers" })
  await app.register(bgpRoutes, { prefix: "/api" })
  await app.register(ospfRoutes, { prefix: "/api" })
  await app.register(execRoutes, { prefix: "/api" })
  await app.register(filtersRoutes, { prefix: "/api" })
  await app.register(recursiveRoutes, { prefix: "/api" })
  await app.register(trafficRoutes, { prefix: "/api" })
  await app.register(trafficFlowRoutes, { prefix: "/api" })
  await app.register(serversApiPingRoutes, { prefix: "/api" })
  await app.register(uptimeRoutes, { prefix: "/api" })
  await app.register(networkRoutes, { prefix: "/api" })
  await app.register(internetPathRoutes, { prefix: "/api" })
  await app.register(evobgpRoutes, { prefix: "/api" })
  await app.register(probesRoutes, { prefix: "/api" })
  await app.register(schedulerRoutes, { prefix: "/api" })
  await app.register(sidebarCountsRoutes, { prefix: "/api" })
  await app.register(alertsRoutes, { prefix: "/api" })
  await app.register(backupsRoutes, { prefix: "/api" })
  await app.register(certificatesRoutes, { prefix: "/api" })
  await app.register(systemDatabaseRoutes, { prefix: "/api" })
  await app.register(eventsRoutes, { prefix: "/api" })
  await app.register(wireguardRoutes, { prefix: "/api" })
  await app.register(firewallRoutes, { prefix: "/api" })
  await app.register(usersRoutes, { prefix: "/api" })

  if (opts?.startScheduler !== false) {
    refreshScheduler()
    startTrafficFlowListener()
    app.addHook("onClose", async () => {
      stopScheduler()
      stopTrafficFlowListener()
    })
  }

  return app
}

const isMain =
  process.argv[1] &&
  (process.argv[1].endsWith("index.ts") || process.argv[1].endsWith("index.js"))

if (isMain) {
  try {
    const app = await buildApp()
    let shuttingDown = false
    const shutdown = async (code: number) => {
      if (shuttingDown) return
      shuttingDown = true
      try {
        stopTrafficFlowListener()
        await app.close()
      } catch (err) {
        console.error(err)
      } finally {
        process.exit(code)
      }
    }
    process.on("SIGTERM", () => { void shutdown(0) })
    process.on("SIGINT", () => { void shutdown(0) })
    process.on("uncaughtException", (err) => {
      console.error(err)
      void shutdown(1)
    })
    process.on("unhandledRejection", (reason) => {
      console.error(reason)
      void shutdown(1)
    })
    await app.listen({ port: env.PORT, host: "0.0.0.0" })
    console.log(
      `\n🚀 MikroTik Manager Backend running at http://localhost:${env.PORT}`,
    )
    console.log(`   Docs / test: http://localhost:${env.PORT}/health`)
  } catch (err) {
    console.error(err)
    process.exit(1)
  }
}
